Google search for the bug, almost can not find the answer to the problem (or I may not find), hereby for the record

Caused by: java.lang.SecurityException: UID 10799 does not have permission to content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FDCIM%2FCamera%2FIMG_20210210_103348.jpg [user 0]

Some time ago, a friend of the project team asked me for help. The profile picture uploading function he was responsible for suddenly failed to work on the Xiaomi phone. At some stage, the system printed the above error log. Take a rough look at the authority problem. According to offline measurement, the problem can be repeated on Mi10, but not on Mi11 and Huawei phones. The first reaction was a bit strange, as it is common sense that if a piece of code is not a system version problem, it should all be wrong. If it’s a system problem, it’s a little trickier.

Before solving the problem, introduce the service scenario. Click the profile picture control, jump to the system album to select the picture, skip to the system cropping program to cut the picture after the selection.

Jump clipper code is shown below

fun gotoSystemCrop(uri: Uri) { val intent = Intent("com.android.camera.action.CROP") intent.setDataAndType(uri, "image/*") grantPermission(intent, uri) intent.putExtra("crop", "true"); Intent. PutExtra ("aspectX", 1) intents. PutExtra ("aspectY", 1) intents. 360) intent.putExtra("outputY", 360) val tmpFile = File(externalCacheDir.toString() + File.separator + "photo" + "_" + System.currentTimeMillis() + ".png") var cropUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { FileProvider.getUriForFile( this, "com.peter.viewgrouptutorial1", tmpFile ) } else { Uri.fromFile(tmpFile) } intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri); grantPermission(intent, cropUri) startActivityForResult(intent, REQUEST_SYSTEM_CROP); }Copy the code

The request authorization code is as follows

private fun grantPermission(intent: Intent, uri: Uri?) {
    var flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
    flag = flag or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
    intent.addFlags(flag)
    val resInfoList = packageManager
        .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
    for (resolveInfo in resInfoList) {
        val packageName = resolveInfo.activityInfo.packageName
        println("xiaozhan $packageName")
        grantUriPermission(packageName, uri, flag)
    }
}
Copy the code

First, analyze the code in the gotoSystemCrop method and call the grantPermission method twice, grantPermission(intent, URI) and grantPermission(Intent, cropUri). We all know that intents have both explicit and implicit invocation. In this case, implicit invocation is used. The latter may match multiple eligible activities. Uri indicates the path of the image returned by the image selection program to the service application. CropUri indicates the storage path of the cropped image to the service application.

The second Assume intent matching to and the name of the package of com. The beautiful miui. Gallery, my business app package called com. Peter. Viewgrouptutorial. Gallery: com.miui.gallery: com.miui.gallery: com.miui.gallery: com.miui.gallery: com.miui.gallery: com.miui.gallery The App is born with read and write permissions to the URIs it exposes. So the first authorization is not required.

Again grantPermission (intent, CropUri) said in com. Peter. Viewgrouptutorial to com in the program. The beautiful miui. Gallery granted access com. Peter. Viewgrouptutorial exposed the Uri of the permissions. In other words, let the clipper finish clipping and save the image to the storage path in the business App. However, you need to request permission to read and write files across apps.

We need to understand that application A can grant read and write permissions to UriA to B and C. C cannot be granted UriB privileges.

Finally, explain the cause of the problem. According to the Intent, the system matches two programs that can crop com.miui.gallery and com.miui.mediaEditor.

  1. In the first grant, the business application grants com.miui.gallery URI read/write permission to com.miui. mediaEditor
 java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { dat=content://com.miui.gallery.open/raw//storage/emulated/0/DCIM/Camera/IMG_20210210_103348.jpg typ=image/jpeg flg=0x1 }} to activity {com.peter.viewgrouptutorial/com.peter.viewgrouptutorial.PhotoActivity}: java.lang.SecurityException: UID 10799 does not have permission to content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FDCIM%2FCamera%2FIMG_20210210_103348.jpg [user 0]
        at android.app.ActivityThread.deliverResults(ActivityThread.java:4940)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:4981)
        at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2046)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:225)
        at android.app.ActivityThread.main(ActivityThread.java:7564)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Copy the code
  1. Comment out the authorization code at 1, the error is similar to the following. However, the ResolverActivity does not have access to the image Uri.
    Process: com.peter.viewgrouptutorial, PID: 3894
    java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { dat=content://com.miui.gallery.open/raw//storage/emulated/0/DCIM/Camera/IMG_20210210_103348.jpg typ=image/jpeg flg=0x1 }} to activity {com.peter.viewgrouptutorial/com.peter.viewgrouptutorial.PhotoActivity}: java.lang.SecurityException: UID 10799 does not have permission to content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FDCIM%2FCamera%2FIMG_20210210_103348.jpg [user 0]
        at android.app.ActivityThread.deliverResults(ActivityThread.java:4940)
        at android.app.ActivityThread.handleSendResult(ActivityThread.java:4981)
        at android.app.servertransaction.ActivityResultItem.execute(ActivityResultItem.java:51)
        at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2046)
        at android.os.Handler.dispatchMessage(Handler.java:107)
        at android.os.Looper.loop(Looper.java:225)
        at android.app.ActivityThread.main(ActivityThread.java:7564)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
     Caused
Copy the code
  1. The final conclusion is that because the clipper is implicitly launched, the application selection screen ResolveActivity is raised, and it does not have access to the Image Uri of the Media application. Then we need to find a way to convert implicit call to explicit call. The solution is as follows
fun gotoSystemCrop(uri: Uri) { val intent = Intent("com.android.camera.action.CROP") intent.setDataAndType(uri, "image/*") intent.putExtra("crop", "true"); Intent. PutExtra ("aspectX", 1) intents. PutExtra ("aspectY", 1) intents. 360) intent.putExtra("outputY", 360) val tmpFile = File(externalCacheDir.toString() + File.separator + "photo" + "_" + System.currentTimeMillis() + ".png") var cropUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { FileProvider.getUriForFile( this, "com.peter.viewgrouptutorial1", tmpFile ) } else { Uri.fromFile(tmpFile) } intent.putExtra(MediaStore.EXTRA_OUTPUT, cropUri); grantPermissionFix(intent, cropUri) startActivityForResult(intent, REQUEST_SYSTEM_CROP); }Copy the code
 private fun grantPermissionFix(intent: Intent, uri: Uri?) {
      var flag = Intent.FLAG_GRANT_READ_URI_PERMISSION
      flag = flag or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
      intent.addFlags(flag)
      val resInfoList = packageManager
          .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
      for (resolveInfo in resInfoList) {
          val packageName = resolveInfo.activityInfo.packageName
          try {
              grantUriPermission(packageName, uri, flag)
          } catch (e: Exception) {
              continue
          }
          intent.action = null
          intent.component =
              ComponentName(resolveInfo.activityInfo.packageName, resolveInfo.activityInfo.name)
          break
      }
  }
Copy the code