1

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)
}
androidguy
  • 3,005
  • 2
  • 27
  • 38

1 Answers1

0

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.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • But I thought, per the documentation, that `FLAG_GRANT_READ_URI_PERMISSION` is only used to signify that you *have* been granted a permission, from the ContentProvider. Is there a mention of this usage (to forward a granted permission) on the developer site? – androidguy Jan 27 '18 at 20:18
  • @user3175580: The primary use of `FLAG_GRANT_READ_URI_PERMISSION` is to grant rights to the content identified by the `Uri` to the component that is the recipient of the `Intent`. The fact that the content isn't "owned" by you does not matter. If you have rights to the content, you can forward those along using `FLAG_GRANT_READ_URI_PERMISSION` and/or `FLAG_GRANT_WRITE_URI_PERMISSION`. – CommonsWare Jan 27 '18 at 20:23
  • @user3175580: "Is there a mention of this usage... on the developer site?" -- well, that's what [the JavaDocs](https://developer.android.com/reference/android/content/Intent.html#FLAG_GRANT_READ_URI_PERMISSION) have. It is also [used by `FileProvider`](https://developer.android.com/reference/android/support/v4/content/FileProvider.html#Permissions), for example (in that case, for granting permissions to other apps). However, the documentation leaves a lot to be desired. :-( – CommonsWare Jan 27 '18 at 20:25