I have a simple app that allows a user to upload data to my service. The user specifies one or more files by a initiating an ACTION_GET_CONTENT or ACTION_IMAGE_CAPTURE, or by receiving a ACTION_SEND. The Uri(s) from getParcleableExtra or getParcleableArrayListExtra are added to an array in UploadActivity, where the get/capture/send intents are received. Some of the Uris are opened with ContentResolver.openInputStream(Uri), or various Cursors to get metadata. This succeeds without any errors. When the user wants to upload the files, the Uris are added to a intent that starts UploadService with putParcleableArrayListExtra. Then startService and finish() are called, closing the activity. UploadService is a IntentService which uploads the files over HTTP. All Uris are opened with Cursors querying their size for the progress bar. They are then, in serial, opened with openInputStream again, then uploaded to the server.
This works perfectly with only 1 Uri. If I have more than 1 Uri, openInputStream throws a SecurityException:
java.lang.SecurityException: Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri content://com.android.externalstorage.documents/document/primary%3Aempty.txt from pid=6062, uid=10300 requires android.permission.MANAGE_DOCUMENTS, or grantUriPermission()
at android.os.Parcel.readException(Parcel.java:1627)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:183)
at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:146)
at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:692)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1111)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:948)
at android.content.ContentResolver.openInputStream(ContentResolver.java:668)
at tk.mii8303.yumeko.YetAnotherURI$OtherURI.Open(YetAnotherURI.java:73)
at tk.mii8303.yumeko.UploadService.onHandleIntent(UploadService.java:119)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:66)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:234)
at android.os.HandlerThread.run(HandlerThread.java:61)
I don't see why I should need MANAGE_DOCUMENTS if I already have called the same method on the file earlier in my app and had it succeed.
I have tried calling
takePersistableUriPermission(uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
grantUriPermission("tk.mii8303.yumeko.UploadService", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
on each Uri before putting them in the intent to no avail.
How do I pass Uris and their permissions to my service?
Related
So I'm testing out a bit of SAF and I'm finding the behaviour of the permissions on the URI to be pretty inconsistent.
Here's how I'm doing it:
I first obtain the tree URI using ACTION_OPEN_DOCUMENT_TREE and using the FLAG_GRANT_WRITE_URI_PERMISSION, FLAG_GRANT_READ_URI_PERMISSION and FLAG_GRANT_PERSISTABLE_URI_PERMISSION when calling on the intent.
In the OnActivityResult, I'm also using the
getContentResolver().takePersistableUriPermission(uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION) to obtain persistence on the obtained uri.
I then store this uri in the sharedpreferences by uri.toString().
Now the issue I'm facing is that when I read the stringified uri back from the shared preferences and parse it to an URI and then try
getContentResolver().getPersistedUriPermissions(), the permissions I get from there are random. Sometimes the list is empty, sometimes it is having the right one. So my question boils down to how long will the permissions really persist? I could not find any documentation on this.
I'm trying this on an emulator. Sometimes I have the permissions even after reboot, but sometimes I lose them after a long idle time on the emulator(without any application running).
Please let me know if I'm missing something.
Thanks in advance.
On Android 10,
I am trying to get data from external apps (like file managers, google photo etc) into my app using this guide -
https://developer.android.com/training/sharing/receive
After receiving the intent in my app, I am trying to open an input stream:
InputStream inputStream = new BufferedInputStream(CGlobals.context.getContentResolver().openInputStream(receivedURI));
It works initially from the Activity specified in the manifest, and even after.
However, I get a Permission Denial error in logcat-
Permission Denial: reading com.google.android.libraries.storage.storagelib.FileProvider uri content://com.google.android.apps.nbu.files.provider/2/721 from pid=19038, uid=10264 requires the provider be exported, or grantUriPermission()
when I am trying to get the input stream from other activities or threads in my app.
After trying for some time, I managed to find that this permission problem appears after I finish the initial activity.
What I can guess from this is, the Android system is granting this permission only as long as the initial activity is not finished.
If what I discovered is true, is this normal or a bug?
Thank you very much.
I'm trying to provide multiple URIs via an Intent with the help of FileProvider from one app to another.
I got it working, when sending only one URI like the following:
resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
resultIntent.setDataAndType(contentUri,getContentResolver().getType(contentUri));
setResult(RESULT_OK, resultIntent);
In my "receiving" app I can continue like this in onActivityResult:
Uri returnUri = data.getData();
ParcelFileDescriptor mInputPFD = getContext().getContentResolver().openFileDescriptor(returnUri, "r");
Now I tried sending an ArrayList of URIs:
resultIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uriList);
resultIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
setResult(RESULT_OK, resultIntent);
but the granted permission flag only applies to the URI set in setData(), so I can't access the URIs from the ArrayList in my receiving app. I read about ClipData as a solution, but I'm unfortunatley forced to go with Min SDK 15.
So my question is, is it a good idea to set the permissions manually in my receiving app with something like
context.grantUriPermission("com.example.provider", returnUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
for every URI i would get if I chose the "ArrayList" option and revoke it later on? Can I do this at all? Couldn't every other app access the provider app's private files like that also?
And what package do I have to specify here? My provider app or my receiver app package, since I'm not getting this to work and only get some exception about missing permission.
Any help or hints are appreciated
Solved it.
My problem was, that I called grantUriPermission() in my receiver app and not in the provider app as it should be. So calling the following fixed the permission exceptions:
getApplicationContext().grantUriPermission(getCallingPackage(), fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
I'm little bit puzzled about grantUriPermission function. I'd like to use it to grant file access to another application via a FileProvider. Since FileProvider must be local (non-exported) for good reason, I must grant the other app temporary read/write permission. Documentation says:
Normally you should use Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION with the Intent being used to
start an activity instead of this function directly. If you use this
function directly, you should be sure to call revokeUriPermission(Uri,
int) when the target should no longer be allowed to access it.
Fair enough, since I don't need to open an activity nor explicitly start a service (the other app would just ask for that file when its time comes), I though I'm fine with just plain:
Uri newFileUri = FileProvider.getUriForFile(this, AUTHORITY, file);
grantUriPermission(OTHER_APP_PACKAGE_NAME, newFileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
To my surprise, I was still getting the security exception:
java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{42ee0b98 5878:example.com.myapplication3/u0a82} (pid=5878, uid=10082) that is not exported from uid 10081
After few experiments I found out that I must call the other app via an Intent at least once:
Intent intent = new Intent();
intent.setClassName(OTHER_APP_PACKAGE_NAME, OTHER_APP_SERVICE_NAME);
startService(intent);
As soon as I do this, I can do everything as expected -- I can remove this intent code, I can even reinstall both apps and it still works (!). It seems like there's a hidden requirement that the owner must call the other app at least once and its stored somewhere in the system.
Is this documented somewhere?
EDITED: first I thought I have to use Intent.FLAG_GRANT_READ_URI_PERMISSION flag, too but this seems to be not necessary.
I've recently set my Android app up to receive Intents so it can be used to open attachments from email apps.
This works fine when my app is not already running when the Intent is created, but when my app is paused in the background, attempting to read the content from the URI causes SecurityExceptions to be thrown:
E/AndroidRuntime(28250): java.lang.RuntimeException: Unable to resume activity
{com.[...]/com.[...].Main}: java.lang.SecurityException: Permission Denial:
reading com.google.android.gm.provider.MailProvider uri content://gmail-ls/
notateme#gmail.com/messages/134/attachments/0.1/BEST/false from pid=28250,
uid=10205 requires com.google.android.gm.permission.READ_GMAIL, or
grantUriPermission()
E/AndroidRuntime(28250): Caused by: java.lang.SecurityException: Permission
Denial: reading com.google.android.gm.provider.MailProvider uri content://
gmail-ls/notateme#gmail.com/messages/134/attachments/0.1/BEST/false from
pid=28250, uid=10205 requires com.google.android.gm.permission.READ_GMAIL,
or grantUriPermission()
This web post suggests that the problem is that my activity has android:launchMode="singleTask" set, and that the way to solve the problem is to create an intermediary Activity to handle the Intent.
That fix does indeed work for me, but I'd like to understand why my original Activity appeared to have the permission to read from the URI when it was created to handle the Intent, but not when it was resumed to handle the Intent.
Could anyone explain this for me?
You need to add READ_GMAIL permission in manifest.
<uses-permission android:name="com.google.android.gm.permission.READ_GMAIL" />
Refer this Manifest Permissions