Search code examples
androidandroid-intentandroid-contentprovider

How to inspect ContentProvider available for Intent (or block specific one)


I use the Intent mechanism to have the user select an image via the standard way

    val intent = Intent(Intent.ACTION_GET_CONTENT)
    intent.type = "image/*"
    intent.addCategory(Intent.CATEGORY_OPENABLE)
    ctx.startActivityForResult(intent, RequestCodes.SelectPhoto)

then I pass the Uri to another activity to maybe crop the photo. I need the Uri before to do some pre-checks.

On the Android emulators, the default providers such as Photos (apparently) give my whole app permission to open the Uri, not just the requesting activity. However, there is a "weird" provider in Asia, com.miui.gallery.provider.GalleryOpenProvider that doesn't -- an evil SecurityException happens in the cropper.

So I try to use ACTION_OPEN_DOCUMENT, which per the specs say that it will give my whole app permission until device reboot, but unfortunately that one doesn't support Google Photos in the cloud, in the emulator.

So I am looking for a way to determine if com.miui.gallery.provider.GalleryOpenProvider is going to be on the list for GET_CONTENT, and if so either prevent it, or otherwise fall back to using ACTION_OPEN_DOCUMENT. I'd like to avoid copying the stream before giving the Uri to the cropper, the crop activity treats it as readonly anyway.

This the full function to start the crop (kotlin). CropActivity is a modification of the old open-source Gallery app com.android.gallery3d.

private fun startCrop(ctx: Activity, uri: Uri) {
    val intent = Intent(ctx, CropActivity::class.java)
    intent.data = uri
    val file = this.createImageFile(ctx, "photofinal")
    if (file == null) {
        this.showStorageUnavailable(ctx)
        return
    }
    val outputUri = Uri.fromFile(file)
    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri)
    intent.putExtra(CropExtras.KEY_MIN_CROP_SIDE, Config.minimumImageDimension)
    intent.putExtra(CropExtras.KEY_MOST_OBLONG_ASPECT, Config.maxPhotoAspectRatio)
    intent.putExtra(CropExtras.KEY_EXIF_ORIENTATION, exifOrientation)
    ctx.startActivityForResult(intent, RequestCodes.CropPhoto)
}

Solution

  • then I pass the Uri to another activity to maybe crop the photo

    Pass that Uri in the "data" facet of the Intent, and add FLAG_GRANT_READ_URI_PERMISSION to transfer read access to the other component. See this sample app:

      @Override
      public void onActivityResult(int requestCode, int resultCode,
                                   Intent resultData) {
        if (resultCode==Activity.RESULT_OK) {
          getActivity()
            .startService(new Intent(getActivity(), DurablizerService.class)
              .setData(resultData.getData())
              .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION));
        }
      }
    

    Here, I happen to be passing the Uri to a service, but the same principle holds for an activity.

    See also this blog post for more about Uri access lifetimes.

    Or, don't use separate activities, but do something else (e.g., multiple fragments).

    On the Android emulators, the default providers such as Photos (apparently) give my whole app permission to open the Uri, not just the requesting activity.

    That would occur if the Uri has a file scheme or is from an exported permission-less ContentProvider.

    So I try to use ACTION_OPEN_DOCUMENT, which per the specs say that it will give my whole app permission until device reboot

    It is subject to the same general rules as the Uri values you get from ACTION_GET_CONTENT.

    So I am looking for a way to determine if com.miui.gallery.provider.GalleryOpenProvider is going to be on the list for GET_CONTENT

    That's not strictly possible. Any app could return a Uri from that provider. In practice, that provider may only be used by its hosting app. If you found the package name for that provider's app, and you used queryIntentActivities() on PackageManager with your ACTION_GET_CONTENT Intent, you could determine if an activity from that app is in the list of ACTION_GET_CONTENT implementations.

    However, if you use FLAG_GRANT_READ_URI_PERMISSION, as I note earlier, that should not be necessary.

    if so either prevent it

    Other than by rolling your own "chooser"-style UI, that's not strictly possible.