2

This is my first post on here, so if my etiquette isn't correct please let me know and I will update.

We made an app for tracking data related to a pet. When setting up the pet, you can either snap a new picture, or use one from your gallery. Works flawlessly on my Samsung s9 (Android 9 - API 28). We are running into a SecurityException on API 29 that I can't seem to figure out. Here is the crash report:

    java.lang.RuntimeException: 
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3163)
  at android.app.ActivityThread.handleLaunchActivity (ActivityThread.java:3306)
  at android.app.servertransaction.LaunchActivityItem.execute (LaunchActivityItem.java:78)
  at android.app.servertransaction.TransactionExecutor.executeCallbacks (TransactionExecutor.java:108)
  at android.app.servertransaction.TransactionExecutor.execute (TransactionExecutor.java:68)
  at android.app.ActivityThread$H.handleMessage (ActivityThread.java:1994)
  at android.os.Handler.dispatchMessage (Handler.java:106)
  at android.os.Looper.loop (Looper.java:216)
  at android.app.ActivityThread.main (ActivityThread.java:7263)
  at java.lang.reflect.Method.invoke (Native Method)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:494)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:975)
Caused by: java.lang.SecurityException: 
  at android.os.Parcel.createException (Parcel.java:1966)
  at android.os.Parcel.readException (Parcel.java:1934)
  at android.os.Parcel.readException (Parcel.java:1884)
  at android.app.IActivityManager$Stub$Proxy.getContentProvider (IActivityManager.java:4029)
  at android.app.ActivityThread.acquireProvider (ActivityThread.java:6506)
  at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider (ContextImpl.java:2825)
  at android.content.ContentResolver.acquireUnstableProvider (ContentResolver.java:1835)
  at android.content.ContentResolver.openTypedAssetFileDescriptor (ContentResolver.java:1449)
  at android.content.ContentResolver.openAssetFileDescriptor (ContentResolver.java:1302)
  at android.content.ContentResolver.openInputStream (ContentResolver.java:1022)
  at android.provider.MediaStore$Images$Media.getBitmap (MediaStore.java:920)
  at com.reptibox.reptileapp.EditReptileData.getReptileImage (EditReptileData.java:228)
  at com.reptibox.reptileapp.EditReptileData.getReptileInfo (EditReptileData.java:298)
  at com.reptibox.reptileapp.EditReptileData.onCreate (EditReptileData.java:197)
  at android.app.Activity.performCreate (Activity.java:7353)
  at android.app.Activity.performCreate (Activity.java:7344)
  at android.app.Instrumentation.callActivityOnCreate (Instrumentation.java:1275)
  at android.app.ActivityThread.performLaunchActivity (ActivityThread.java:3143)
Caused by: android.os.RemoteException: 
  at com.android.server.am.ActivityManagerService.getContentProviderImpl (ActivityManagerService.java:15890)
  at com.android.server.am.ActivityManagerService.getContentProviderImpl (ActivityManagerService.java:15799)
  at com.android.server.am.ActivityManagerService.getContentProvider (ActivityManagerService.java:16364)
  at android.app.IActivityManager$Stub.onTransact$getContentProvider$ (IActivityManager.java:11085)
  at android.app.IActivityManager$Stub.onTransact (IActivityManager.java:295)

This is the method that the report points to:

public Bitmap getReptileImage(){
    Cursor cursor = mDB.rawQuery("SELECT * FROM " + ReptileContract.ReptileImageEntry.TABLE_NAME +
            " WHERE " + ReptileContract.ReptileImageEntry._ID + "='" + mReptileImageId +
            "' ORDER BY " + ReptileContract.ReptileImageEntry._ID + " DESC;", null);

        cursor.moveToFirst();

        if (cursor.getCount() > 0) {
            try {
                Uri mImageURI = Uri.parse(cursor.getString(cursor.getColumnIndex(ReptileContract.ReptileImageEntry.COLUMN_REPTILE_URI)));
                try{
                        if (android.os.Build.VERSION.SDK_INT >= 29){
                            // To handle deprecation use
                            return ImageDecoder.decodeBitmap(ImageDecoder.createSource(getContentResolver(),mImageURI));
                        } else{
                            // Use older version
                            //SECURITY EXCEPTION HERE
                            return android.provider.MediaStore.Images.Media.getBitmap(getContentResolver(), mImageURI);
                        }
                   // }
                }
                catch(IOException e){
                    Log.e("IMAGE LOADING", "IOException Error Caught");
                    e.printStackTrace();
                    cursor.close();
                    return null;
                }
            }
            catch (NullPointerException e){
                Log.e("IMAGE LOADING", "Normal for first loading, since nothing has URI's");
                e.printStackTrace();
                cursor.close();
                return null;
            }
        }
        else {
            cursor.close();
            return null;
        }
}

Please let me know if you need more here. I am looking to resolve this crash, and anything that can point me int he right direction would be amazing!

  • Does it load the image successfully the first time then trigger the error while trying to load the same image later? – Sammy T Dec 10 '19 at 04:14
  • @DustinGunter, here it must be crashing `android.provider.MediaStore.Images.Media.getBitmap(getContentResolver(), mImageURI);`. Try to change from 29 to this one `Build.VERSION.SDK_INT >= 23`, as you might be trying to load the bitmap on above Marshmellow device – ॐ Rakesh Kumar Dec 10 '19 at 04:39
  • 1
    @Sammy T that is exactly correct, everything works well upon initial install and then is crashing a couple days later – Dustin Gunter Dec 10 '19 at 11:41

1 Answers1

0

It's a weird issue with the type of Uri that you're trying to access. You're getting a content type Uri which has some limitations.

You can usually store and access a Uri of type file without issue. However, a content Uri has a limited time which it can be accessed.

Your options are pretty limited here but this is how I've gotten around it. You can copy the file the user has selected to a directory that your app has full access to (ex. getFilesDir()), store the path to the copied file, and have your app access the copy instead of the original.

Check out this answer for some more detail.

Sammy T
  • 1,924
  • 1
  • 13
  • 20