17

In my application I store the path of image in my SQlite db for further use. The path that I get is

content://com.android.providers.media.documents/document/image%3A71964

When I retrieve this path from the database and try to retrieve the image from that path android throws

java.lang.SecurityException: Permission Denial: opening provider
com.android.providers.media.MediaDocumentsProvider 
from ProcessRecord{42c84ec8 23911:com.gots.gb/u0a248} (pid=23911, uid=10248) 
requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS

According https://developer.android.com/guide/topics/providers/document-provider.html#permissions I need to persist permission by adding the following code

final int takeFlags = intent.getFlags()
        & (Intent.FLAG_GRANT_READ_URI_PERMISSION
        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(uri, takeFlags);

When I added this code to my ImageAdapter class which extends BaseAdapter android throws

08-21 02:14:38.530: W/System.err(24452): java.lang.SecurityException:
No permission grant found for UID 10248 and Uri 
content://com.android.providers.media.documents/document/image:71964

This is the relevant part of my ImageAdapter code

public View getView(int position, View convertView, ViewGroup parent) {
    ImageView imageView ;


    if (convertView == null){
        imageView = new ImageView(mContext);
        imageView.setLayoutParams(new GridView.LayoutParams(185, 185));
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setPadding(8, 8, 8, 8);

    }
    else{
        imageView = (ImageView)convertView ;
    }

    InputStream is = null;
    Bitmap bitmap = null ;

    try {

        Log.d(TAG,String.valueOf(list.get(position)));
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        final int takeFlags = intent.getFlags()
                & (Intent.FLAG_GRANT_READ_URI_PERMISSION
                | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        // Check for the freshest data.

        if (Build.VERSION.SDK_INT >= 19){
            mContext.getContentResolver().takePersistableUriPermission(list.get(position), takeFlags);
        }


        is = mContext.getContentResolver().openInputStream(list.get(position));

        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 8;
        bitmap = BitmapFactory.decodeStream(is,null, options);
        is.close();
        imageView.setImageBitmap(bitmap);

        return imageView;

    }
    catch (Exception e) {
        e.printStackTrace();

        return null;
    }

what am I doing wrong? Thanks

Pdksock
  • 1,042
  • 2
  • 13
  • 26
  • 3
    Try calling `takePersistableUriPermission()` when you get the `Uri` originally, not when you are trying to use the `Uri` later. Also, please do not invoke a `ContentResolver` on the main application thread, as you are doing in a couple of spots in `getView()`. In particular, `openInputStream()` and your `BitmapFactory` work definitely needs to be moved to a background thread. – CommonsWare Aug 20 '14 at 22:58
  • @CommonsWare I get this error `Requested flags 0x1, but only 0x0 are allowed` error. I am trying to retrieve an image from the gallery using `ACTION_GET_CONTENT` . – Pdksock Aug 21 '14 at 06:27
  • I haven't played with this stuff yet (it's high on my to-do list), but my guess is that the Gallery is not offering you a persistable permission. – CommonsWare Aug 21 '14 at 11:15
  • @Pdksock change this: final int takeFlags = Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION; – Simon Jun 26 '21 at 02:32

2 Answers2

32

I believe I've solved it. The request intent:

Intent intent;
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
    intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
    intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
}else{
    intent = new Intent(Intent.ACTION_GET_CONTENT);
}
intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setType("image/*");
startActivityForResult(Intent.createChooser(intent, getResources().getString(R.string.form_pick_photos)), REQUEST_PICK_PHOTO);

and onActivityResult

...
// kitkat fixed (broke) content access; to keep the URIs valid over restarts need to persist access permission
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    final int takeFlags = data.getFlags() & Intent.FLAG_GRANT_READ_URI_PERMISSION;
    ContentResolver resolver = getActivity().getContentResolver();
    for (Uri uri : images) {
        resolver.takePersistableUriPermission(uri, takeFlags);
    }
}
...

I haven't tested this pre-kitkat, my phone is running 5.1, can anyone verify this on older phones?

DariusL
  • 4,007
  • 5
  • 32
  • 44
  • 3
    Very clean and good solution. There is one thing: `Utils.isKitkat()` is not available without additional libraries. Use `Build.VERSION.SDK_INT >= 19` instead – Erich Jul 14 '15 at 17:04
  • Yes, the method does a simple check against the build version. – DariusL Jul 14 '15 at 17:39
  • Adding to @Erich comment, you can use Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT to make it a bit more readable. – user1689757 Sep 09 '15 at 15:13
  • I get an error saying No persistable permission grants found for UID xxxx and Uri 0 @ content://media/external/video/media/xxxx.. what might be the problem ?? TIA – Kiran Ruth R Dec 19 '15 at 09:28
  • How long so you keep the URIs? I think I got that when I used a URI across reinstalls. – DariusL Dec 19 '15 at 13:15
  • Accept this as the answer! – Flyview Jan 21 '16 at 19:12
  • how to get file path from uri in this case – Ram Apr 07 '17 at 07:36
  • @Ram you can't, that's just how android content URIs work. It is possible that they don't even point to a file, but that's not very likely. Use `openInputStream` from the content resolver. If you really need the file, dump that stream to a new file. – DariusL Apr 07 '17 at 07:53
  • @DariusL what is the value of REQUEST_PICK_PHOTO constant decleration ? – Engin Ardıç Mar 11 '19 at 09:36
  • IIRC, that can be whatever you want. When you get a response, you'll get that value back, so you can figure out which response is for which request. – DariusL Mar 12 '19 at 10:56
0

The code listed below,

// kitkat fixed (broke) content access; to keep the URIs valid over restarts need to persist access permission
if(Utils.isKitkat())
{
    final int takeFlags = data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION);
    ContentResolver resolver = getActivity().getContentResolver();
    for (Uri uri : images)
    {
        resolver.takePersistableUriPermission(uri, takeFlags);
    }
}

worked fine for me, anyway I noticed that the max number of persistable uri granted to my app is limited to 128. If I select more than 128 uri, I get the error:

java.lang.SecurityException: Permission Denial: opening provider........

just when I try to process an image for which I wasn't able to persist the permission. Can you figure out any solution?

David
  • 1
  • 1
    Please don't ask questions in answers. If it fits with the question add a comment to the question otherwise open a new question. – Evan Frisch Jun 11 '15 at 19:34
  • If you don't actually need all 128 URIs at once then perhaps you should call [`ContentResolver.releasePersistableUriPermission()`](https://developer.android.com/reference/android/content/ContentResolver.html#releasePersistableUriPermission(android.net.Uri,%20int)) on the ones you don't need... – snark Nov 04 '17 at 19:18