Search code examples
androidpython-3.xpermissionskivybuildozer

Can't retrieve image from a url link


I am working on an app on Kivy and push it to Android. On the PC, my app can perfectly display an image from a google firebase url. However, on the Android phone, it runs into an SSL error. I assume it's an issue in the buildozer spec permission. I already gave it INTERNET permission. I'm not sure whatelse it needs to display an image from the web. Here's the code in python and kv: Python:

cred = credentials.Certificate("json key file name")
app = firebase_admin.initialize_app(cred,{ 'storageBucket': 'link to firebase storage.appspot.com'}, name ='storage')
bucket = storage.bucket(app=app)
imageBlob = taker2.bucket.blob(str(SelectableLabel.itemkey))
self.url = imageBlob.generate_signed_url(datetime.timedelta(seconds=300), method='GET')
print(self.url)

Kivy:

AsyncImage:
      id: image            
      source: root.url

Error:

[INFO   ] [Factory     ] 184 symbols loaded
[INFO   ] [Image       ] Providers: img_tex, img_dds, img_sdl2, img_gif (img_pil, img_ffpyplayer ignored)
[INFO   ] [Text        ] Provider: sdl2
[INFO   ] [Window      ] Provider: sdl2
[INFO   ] [GL          ] Using the "OpenGL ES 2" graphics system
[INFO   ] [GL          ] Backend used <sdl2>
[INFO   ] [GL          ] OpenGL version <b'OpenGL ES 3.1 V@139.0 (GIT@I8a1ccf9ecb)'>
[INFO   ] [GL          ] OpenGL vendor <b'Qualcomm'>
[INFO   ] [GL          ] OpenGL renderer <b'Adreno (TM) 418'>
[INFO   ] [GL          ] OpenGL parsed version: 3, 1
[INFO   ] [GL          ] Texture max size <16384>
[INFO   ] [GL          ] Texture max units <16>
[INFO   ] [Window      ] auto add sdl2 input provider
[INFO   ] [Window      ] virtual keyboard not allowed, single mode, not docked
[INFO   ] [GL          ] NPOT texture support is available
[INFO   ] [Loader      ] using a thread pool of 2 workers
[INFO   ] [Camera      ] Provider: android
[WARNING] [Base        ] Unknown <android> provider
[INFO   ] [Base        ] Start application main loop
https://storage.googleapis.com/python-app-4857e.appspot.com/-LmrOE_8MwiGdeaYKJ-j?Expires=1566449320&GoogleAccessId=firebase-adminsdk-tlrv0%40python-app-4857e.iam.gserviceaccount.com&Signature=o4jTL7eKqBTl5roNiwnNTCrq15RQEms91UBuVzPzMXKUqAJHH9LcygDS1OQOKVSKInxpm%2FXvUGeEbpAWKtUq50vf14LN8FGzGJTBkvYieiHCMP4n8sD4IN%2Fgj2nbGLCdcQKyANObOzWVKOZ%2BwNs7dpAs04ZQ5RQ05CJLqQ4wLopM9R3A9zgKUnZFmHaF6FkjwaRU1ogKdA%2BmQdV%2FP2NdfJ41aKACsIEWLTYZdrL1gCrVcQ7l5%2FHBzL2yHSdMqQL%2BFAohUc2c6D2o2W7tTIHEmxLlo%2BgdLlwOth%2BG5ImYGasbgG9xwf3ROpVrGtkfrbCznhEI8DynpNuqcqAdpb%2BKxw%3D%3D
[ERROR  ] [Loader      ] Failed to load image <https://storage.googleapis.com/python-app-4857e.appspot.com/-LmrOE_8MwiGdeaYKJ-j?Expires=1566449320&GoogleAccessId=firebase-adminsdk-tlrv0%40python-app-4857e.iam.gserviceaccount.com&Signature=o4jTL7eKqBTl5roNiwnNTCrq15RQEms91UBuVzPzMXKUqAJHH9LcygDS1OQOKVSKInxpm%2FXvUGeEbpAWKtUq50vf14LN8FGzGJTBkvYieiHCMP4n8sD4IN%2Fgj2nbGLCdcQKyANObOzWVKOZ%2BwNs7dpAs04ZQ5RQ05CJLqQ4wLopM9R3A9zgKUnZFmHaF6FkjwaRU1ogKdA%2BmQdV%2FP2NdfJ41aKACsIEWLTYZdrL1gCrVcQ7l5%2FHBzL2yHSdMqQL%2BFAohUc2c6D2o2W7tTIHEmxLlo%2BgdLlwOth%2BG5ImYGasbgG9xwf3ROpVrGtkfrbCznhEI8DynpNuqcqAdpb%2BKxw%3D%3D>
Traceback (most recent call last):
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/urllib/request.py", line 1317, in do_open
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/http/client.py", line 1229, in request
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/http/client.py", line 1275, in _send_request
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/http/client.py", line 1224, in endheaders
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/http/client.py", line 1016, in _send_output
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/http/client.py", line 956, in send
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/http/client.py", line 1392, in connect
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/ssl.py", line 412, in wrap_socket
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/ssl.py", line 853, in _create
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/ssl.py", line 1117, in do_handshake
ssl.SSLCertVerificationError: [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1051)

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/python-installs/myapp/kivy/loader.py", line 342, in _load_urllib
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/urllib/request.py", line 525, in open
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/urllib/request.py", line 543, in _open
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/urllib/request.py", line 503, in _call_chain
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/urllib/request.py", line 1360, in https_open
  File "/home/hcao/Desktop/test3/.buildozer/android/platform/build/build/other_builds/python3-libffi-openssl-sqlite3/armeabi-v7a__ndk_target_21/python3/Lib/urllib/request.py", line 1319, in do_open
urllib.error.URLError: <urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1051)>

So my app opens up a connection with firebase server for 5 mins. Within this timeframe, the url link would be able to display an image. And this image can be used to display in the app. It works perfectly on PC. On Android, it shows empty image because of the SSL Certificate error. However, if I manually copy that link in that error message and paste it into a browser, I would see an image just fine. So I must set a new permission in buildozer which I am not sure what. My current buildozer permission:

Permissionsandroid.permissions = INTERNET

Update 8/23/2019: I added android:usesCleartextTraffic="true"to the AndroidManifest.xml file as one suggested. That did not fix the issue. My app is still unable to display the image from the url on the android phone. It's working fine on PC. Here's my manifest file:

<?xml version="1.0" encoding="utf-8"?>
<!-- Replace com.test.game with the identifier of your game below, e.g.
     com.gamemaker.game
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="org.libsdl.app"
    android:versionCode="1"
    android:versionName="1.0"
    android:installLocation="auto">

    <!-- OpenGL ES 2.0 -->
    <uses-feature android:glEsVersion="0x00020000" />

    <!-- Touchscreen support -->
    <uses-feature
        android:name="android.hardware.touchscreen"
        android:required="false" />

    <!-- Game controller support -->
    <uses-feature
        android:name="android.hardware.gamepad"
        android:required="false" />

    <!-- External mouse input events -->
    <uses-feature
        android:name="android.hardware.type.pc"
        android:required="false" />

    <!-- Allow writing to external storage -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <!-- Allow access to the vibrator -->
    <uses-permission android:name="android.permission.VIBRATE" />

    <!-- if you want to capture audio, uncomment this. -->
    <!-- <uses-permission android:name="android.permission.RECORD_AUDIO" /> -->

    <!-- Create a Java class extending SDLActivity and place it in a
         directory under app/src/main/java matching the package, e.g. app/src/main/java/com/gamemaker/game/MyGame.java

         then replace "SDLActivity" with the name of your class (e.g. "MyGame")
         in the XML below.

         An example Java class can be found in README-android.md
    -->
    <application android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher"
        android:allowBackup="true"
        android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
        android:usesCleartextTraffic="true"
        android:hardwareAccelerated="true" >

        <!-- Example of setting SDL hints from AndroidManifest.xml:
        <meta-data android:name="SDL_ENV.SDL_ACCELEROMETER_AS_JOYSTICK" android:value="0"/>
         -->

        <activity android:name="SDLActivity"
            android:label="@string/app_name"
            android:configChanges="keyboard|keyboardHidden|orientation|screenSize"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <!-- Drop file event -->
            <!--
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="*/*" />
            </intent-filter>
            -->
        </activity>
    </application>

</manifest>

Solution

  • After a week of research, I finally found the solution posted at https://github.com/kivy/python-for-android/issues/1827 Simple add these lines to main.py

    import certifi
    import os
    
    # Here's all the magic !
    os.environ['SSL_CERT_FILE'] = certifi.where()