mario595
mario595

Reputation: 3761

Getting Permission Denial Exception

I have an activity in my app that allows the user to select several files from the device one by one, I am using an intent like this:

Intent intent = new Intent();
intent.setType("*/*");
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent, getString(R.string.select_attachments_activity_chooser_label)), SELECT_PICTURE);

This is working perfectly fine, I am getting the Uri's of the files selected, they look like this:

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

Then, if the file is an image, I am decoding it with:

InputStream streamForDecodeBitmap = MyApp.getContext().getContentResolver().openInputStream(uri);
Bitmap bitmap = BitmapFactory.decodeStream(streamForDecodeBitmap, null, options);

When the user clicks a button, I pass the list of Uris to another activity via intent and in this activity, in an AsyncTask, I am encoding the file in base64 for send it over the network:

InputStream is = MyApp.getContext().getContentResolver().openInputStream(uri);
byte[] inputData = getBytes(is);
is.close();
return Base64.encodeToString(inputData, Base64.DEFAULT);

The problem is when I open the inputStream, sometimes it works but most of the times I am getting this exception:

E/AndroidRuntime(22270): Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{42858fe0 22270:co.uk.manifesto.freeagentapp/u0a246} (pid=22270, uid=10246) requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS

These are all the permissions in my manifest:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>

I am testing in a device with KITKAT (API 19).

Upvotes: 24

Views: 30842

Answers (6)

Carlos Lima
Carlos Lima

Reputation: 1

In kotlin solve by placing the operator "?" = (nullable) after Intent

as the following code:

override func onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { 
    super.onActivityResult(requestCode, resultCode, data)
}

Upvotes: -3

Al&#233;cio Carvalho
Al&#233;cio Carvalho

Reputation: 13657

Make sure you're using the ContentResolver from the Activity that requested the content, for instance:

activity.getContentResolver()  

Instead of MyApp.getContext().getContentResolver()

It was apparently the case for me and it didn't need to add the android.permission.MANAGE_DOCUMENTS to the manifest.

And that you call the intent using this:

if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
    intent.setAction(Intent.ACTION_GET_CONTENT);
} else {
    intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
}

more info: https://developer.android.com/guide/topics/providers/document-provider.html#client

Upvotes: 10

Knossos
Knossos

Reputation: 16068

Worth noting is that if the user manually removes permissions for a Uri that you have previously saved, then you will get this same Exception.

Android settings -> App -> App Name -> Storage -> Clear Access

You need to check that you still have access to the Uri when you use it.

Upvotes: 0

dhesse
dhesse

Reputation: 3640

Writing ACTION_OPEN_DOCUMENT is not helping entirely. After a restart your URI Permission is gone even with this action.

   if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            intent.setAction(Intent.ACTION_GET_CONTENT);
        } else {
            intent.setAction(Intent.ACTION_OPEN_DOCUMENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
        }

To complete Alécio Carvalho's answer look at this documentation on the same page. There is a good explanation regarding URI Permissions and how to make them persist.

Just add this to your onActivityResult method and your permission persists through a device restart. (Tested with a Google Pixel.)

override fun onActivityResult(requestCode:Int, resultCode:Int, intent:Intent?) {
if(requestCode == SELECT_PICTURE && intent != null){
    val uri = intent.getData()
    val takeFlags = intent.getFlags() and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
    activity.getContentResolver().takePersistableUriPermission(uri, takeFlags)
    PreferenceHelper.setHeaderImageUri(activity, uri.toString())
  }
}

(sorry kotlin lover...java version is inside the link)

Upvotes: 6

Pararth
Pararth

Reputation: 8134

Please check these questions too:
Android KitKat securityException when trying to read from MediaStore
Permission denial: opening provider

For the same problem on KitKat, I used this. It's an option/workaround I had found from one of the Stack Overflow links, you will be able to select files from Downloads/Recent.

public static final int KITKAT_VALUE = 1002;

Intent intent;

if (Build.VERSION.SDK_INT < 19) {
    intent = new Intent();
    intent.setAction(Intent.ACTION_GET_CONTENT);
    intent.setType("*/*");
    startActivityForResult(intent, KITKAT_VALUE);
} else {
    intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.addCategory(Intent.CATEGORY_OPENABLE);
    intent.setType("*/*");
    startActivityForResult(intent, KITKAT_VALUE);
}

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == KITKAT_VALUE ) {
        if (resultCode == Activity.RESULT_OK) {
            // do something here
        }
    }
}

Reference: Android Gallery on KitKat returns different Uri for Intent.ACTION_GET_CONTENT http://developer.android.com/guide/topics/providers/document-provider.html#client

Upvotes: 37

marc
marc

Reputation: 221

When control and data is returned to your activity in

protected void onActivityResult(int reqCode, int resultCode, Intent data)

your activity, at the same time, is given permissions to access the image url in the passed intent. Your activity will be able to read the url in the intent and display the image. But you wont be able to pass any permissions on to other activities or to a new thread of execution. That is why you are getting the SecurityException for android.permission.MANAGE_DOCUMENTS.

One way to avoid your permission issue, is just to get the data in the activity that has permissions. In your case, from the activity that has permission do your encoding

InputStream is = getContentResolver().openInputStream(uri);
byte[] inputData = getBytes(is);
is.close();
String encoded = Base64.encodeToString(inputData, Base64.DEFAULT);

Once you have your encoded string you can pass it to any other activities that might need it, or you can pass it to an AsyncTask to be sent elsewhere.

Upvotes: 22

Related Questions