Not sure if what I'm trying to do is possible or not. I have a "proxy" document provider meaning a document provider that exports aliases for other content using the SAF. I have a dialog fragment that allows a user to set up the alias by presenting an OPEN_DOCUMENT_TREE intent, capturing the URI, granting persistable permissions on that URI, and then passing that URI to the document provider to present that content.
The dialog fragment is able to read/write on the URI it receives from the intent but the provider portion is not able to. The error I always get is:
java.lang.SecurityException: Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri
I've read many related questions and corresponding replies that folks have posted on working with these persistable READ_URI_PERMISSIONS and have tried all variations suggested in those posts. But I am unable to get this to work. I'm beginning to think that the persistable URI permissions are not available within a provider. Is that the case? Do I need to have the provider invoke an intent so that I can grant these permissions? Perhaps have the provider call into the main activity to access the content. I'd certainly rather access the content directly from the provider. I haven't found anywhere that states once persistable permissions are granted, who are they granted to? Is a provider portion of an app a separate process and is that the issue?
Let me provide relevant code snippets:
The AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.connectedway.connectedsmb">
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
...
<activity ...>
...
</activity>
<provider ...
android:permission="android.permission.MANAGE_DOCUMENTS">
</provider>
</application>
</manifest>
The dialog fragment that obtains the URI is within the main activity and the code that uses the URI is within the provider.
The dialog fragment of the main activity invokes the OPEN_DOCUMENT_TREE intent using:
StorageManager sm = (StorageManager)
getContext().getSystemService(Context.STORAGE_SERVICE);
StorageVolume sv = sm.getPrimaryStorageVolume();
Intent intent = sv.createOpenDocumentTreeIntent();
intent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
onCreateResultLauncher.launch(intent);
The "onCreateResultLauncher" class with the aswsociatd onActivityResult callback is:
ActivityResultLauncher<Intent> onCreateResultLauncher =
registerForActivityResult
(new ActivityResultContracts.StartActivityForResult(),
new ActivityResultCallback<ActivityResult>() {
@Override
public void onActivityResult(ActivityResult result) {
if (result.getResultCode() == Activity.RESULT_OK) {
Intent data = result.getData();
Uri uri = data.getData() ;
getActivity().grantUriPermission(
getActivity().getPackageName(), uri,
Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION |
Intent.FLAG_GRANT_READ_URI_PERMISSION);
final int takeFlags =
data.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION);
getContext().getContentResolver().takePersistableUriPermission(
uri, takeFlags);
// PASS THE URI OFF TO THE PROVIDER
}
}
}
});
Within the dialog fragment, I test the URI to see if I can read it:
DocumentFile file = DocumentFile.fromTreeUri(getContext(), uri);
if (file.canRead())
System.out.println ("Can Read");
We always can read. Now the provider tries to access the URI as follows:
@Override
public Cursor queryDocument(String documentId, String[] projection)
throws FileNotFoundException {
// convert the documentId passed in to a uri. The conversion process
// results in the same URI as that received by the OPEN_DOCUMENT_TREE
// intent invoked and tested above.
DocumentFile file = DocumentFile.fromTreeUri(getContext(), uri);
if (file.canRead())
System.out.println ("Can Read");
The provider does not have read access. So when the provider eventually does a query on the URI, it will fail with the SecurityException shown above.
The questions are:
- Is this supported architecturally?
- If so, can anyone see any obvious mistake in the above snippets?
- If not, can anyone think of a way I can work around the architectural limitation?
Thank you for reading through the post.