Search code examples
androidandroid-intentstorage-access-framework

SecurityException on Intent.ACTION_VIEW while trying to open PDF


I ask a user to choose a PDF document using ACTION_OPEN_DOCUMENT. The intent returns a content URI.

Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
    intent.setType("application/pdf");
    intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
    intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
    intent.addCategory(Intent.CATEGORY_OPENABLE);

The content URI is in the format - content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2FSample%20PDF.pdf

Now, I want the user to access the same document again - later on, from a separate activity. So I write the following code -

Intent pdfIntent = new Intent(Intent.ACTION_VIEW);
                    pdfIntent.setData(document.getLocalUri());
                    pdfIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    mContext.startActivity(pdfIntent);

But I get the following error -

java.lang.SecurityException: UID 10197 does not have permission to content://com.android.providers.downloads.documents/document/raw%3A%2Fstorage%2Femulated%2F0%2FDownload%2FSample%20PDF.pdf [user 0]; you could obtain access using ACTION_OPEN_DOCUMENT or related APIs

What am I doing wrong?


Solution

  • What am I doing wrong?

    Most likely, your problem lies here:

    I want the user to access the same document again - later on, from a separate activity

    The activity that requested and received the Uri to the content has rights to that content... but nothing else of your app does. That includes other components in your current process any any components in future processes.

    If this is a separate activity, but still in the same app instance (i.e., your process has not been terminated since when you got the Uri), you need to pass the rights to that content to the separate activity. Pass the Uri via the "data" facet of the Intent and use setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) as you are doing with the ACTION_VIEW Intent. See this sample app, where I use FLAG_GRANT_READ_URI_PERMISSION to grant rights to a Service to be able to read the content.

    If your intention is to persist the Uri value itself (e.g., store it as a string in a database) and use it in the future, you can instead use takePersistableUriPermission() on ContentResolver to request long-term access to the content. This should succeed (I don't know of any scenario where you're immediately denied access). You should have access until something drastic happens to the content, such as the user deleting it.