Android 10 : Not able to use openFileDescriptor inside IntentService - android

I need to upload an attachment chosen by the user through our app. The upload works when it is called within the Activity that accepted the URI selected by user. But when I pass the URI to an IntentService so that a huge attachment could be uploaded in the background, I get a "Permission Denial" exception in the IntentService in the following line -
final Uri uri = intent.getData();
//This line works within Activity but throws "Permission Denial" exception in IntentService
ParcelFileDescriptor inputPFD = getContentResolver().openFileDescriptor(uri, "r");
FileDescriptor fd = inputPFD.getFileDescriptor();
final FileInputStream fileInputStream = new FileInputStream(fd);
int bytesAvailable = fileInputStream.available();
Stacktrace
-----------
java.lang.SecurityException: Permission Denial: opening provider com.android.externalstorage.ExternalStorageProvider from ProcessRecord{6148698 8180:com.<package name>/u0a140} (pid=8180, uid=10140) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
at android.os.Parcel.createException(Parcel.java:2071)
at android.os.Parcel.readException(Parcel.java:2039)
at android.os.Parcel.readException(Parcel.java:1987)
at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:5054)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:6561)
at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2725)
at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:2117)
at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1671)
at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1503)
at android.content.ContentResolver.openFileDescriptor(ContentResolver.java:1338)
at android.content.ContentResolver.openFileDescriptor(ContentResolver.java:1286)
Please let me know what I might be doing wrong. If openFileDescriptor cannot be used on a URI that was not received by the original Activity, please suggest a good approach to upload huge attachment in the background as the user might move out of the activity after choosing the file to upload. Appreciate your help.

Solved it. The fix is what #CommonsWare suggested - Include FLAG_GRANT_READ_URI_PERMISSION on the Intent that you use to launch the IntentService. By default, only your activity has access to the content identified by the Uri

Related

Android Content provide leading to SecurityException: Permission Denial if not used immediately

In my Android Manifest I registered my Activity for being able to "open" specific files.
This all works fine. When I receive the new Intent and read the file data from the intent via
BufferedReader(InputStreamReader(contentResolver.openInputStream(intent.data)))
it works fine.
However, for a better view flow, I wanted to store the URI of the intent and show it in another view (asking the user how to proceed with the file). So I store this intent.data as a String and open another view first.
However, once the view is opened, I bascially call the same thing
val br = BufferedReader(InputStreamReader(act.contentResolver.openInputStream(fileUri)))
but here I get an exception
java.lang.SecurityException: Permission Denial: reading [FileBrowserApp I used for "opening the file"] uri content://... from pid=5242, uid=10159 requires the provider be exported, or grantUriPermission()
So it feels like the URI is somehow expired or such thing? Is this actually the case? Do I have to read the file directly when I receive the Intent? I was hoping for a way to keep the URI until I want to read the file.
I found the issue. Turns out, it is not a problem of an expiring Intent or anything like that. Instead my own "processing" changed the path.
What I did was taking the incoming Intent data as Uri. Later I fetched the provider path from that Uri again. However this caused a transformation of the query (instead of leaving it as a String in the first place) breaking the path.
In short: the problem is the URL encoding/decoding.
The original intent path (opened via the TotalComander - hence the com.ghisler path) looked like this:
content://com.ghisler.files/tree/primary%3A/document/primary%3Astorage%2Femulated%2F0%2Fbackup.bak
However getting the path from the Uri the colons were decoded leaving an output path of this:
content://com.ghisler.files/tree/primary:/document/primary:storage/emulated/0/backup.bak
You can clearly see the different encoding. As a consequence the file path was simply not the same after fetching it from the Uri again. This was causing the Exception - not an invalidation of the intent.

How can I use file Uri from another app Intent in Android

In my APP I received the Uri of a pdf file from any other app that can share pdfs.
Uri uri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
Anyway I cannot full-use this Uri, I image because of it is from a FileProvider external to my app.
For example when I try to create a FileDescriptor:
ParcelFileDescriptor fd = context.getContentResolver().openFileDescriptor(uri, "r");
I get this error:
W/System.err: java.lang.SecurityException: Permission Denial: opening provider androidx.core.content.FileProvider from ProcessRecord{...} (pid=23606, uid=10525) that is not exported from UID 10123
It is really necessary get the file path, copy the file, and get a new Uri?
Or I can simply manage the original Uri in some way ?
As per my knowledge the best thing to do is to read/copy the data from content provider immediately and then use that data to do whatever you want. so don't waste your time trying to manage the original one. I hope its helpful to you

Android 10: Able to upload a Uri directly but fails with "Permission denial" exception when re-attempted later

Using a FilePicker I am able to have the user choose a file to upload, pass it to an IntentService, and upload it immediately via that intentService if device has network.
But if there is no network, I need to save the Uri and attempt upload later once the devices gets network. This re-attempt is failing. It throws "Permission denial" exception when I try to start the service during the re-attempt. Please let me know what might be wrong. Appreciate your help.
FILEPICKER
-----------
openFilePickDialog = new Intent();
openFilePickDialog.setType("*/*");
openFilePickDialog.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(openFilePickDialog, "Select File"), PickFileId);
During re-attempt of upload
----------------------------
Intent iUploadService = new Intent(context, UploadService.class);
String uriString = pendingUpload.uriString;
iUploadService.setData(Uri.parse(uriString));
iUploadService.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
iUploadService.putExtra(UploadService.ACTION, UploadService.ACTION_UPLOAD);
context.startService(iUploadService);
Stacktrace
-----------
java.lang.SecurityException: UID 10140 does not have permission to content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fdata%2Fcom.aaaa.bbbb%2Ffiles%2FFolder%2FAttachments%2F1587129056397_IMG-20200414-WA0004.jpg [user 0]; you could obtain access using ACTION_OPEN_DOCUMENT or related APIs
at android.os.Parcel.createException(Parcel.java:2071)
at android.os.Parcel.readException(Parcel.java:2039)
at android.os.Parcel.readException(Parcel.java:1987)
at android.app.IActivityManager$Stub$Proxy.startService(IActivityManager.java:5166)
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1601)
at android.app.ContextImpl.startService(ContextImpl.java:1571)
at android.content.ContextWrapper.startService(ContextWrapper.java:669)
Dont use ACTION_GET_CONTENT as as you have seen the permission to read does not live long.
Instead use ACTION_OPEN_DOCUMENT and take persistable uri permission in onActivityResult.

Permission error when trying to pass Content Uri from activity to service

I have an activity which accepts android.intent.action.SEND.
It accepts some media files and texts.
As it accepts a new 'share' it does some validation checks and pass the Uri to a service, it looks like this:
Uri uri = intent.getParcelableExtra(Intent.EXTRA_STREAM);
Intent serviceIntent = new Intent(this, ShareService.class)
.setData(uri)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // probably redundant but harmless here
.putParcelableArrayListExtra(Constants.EXTRA_SHARE_FILES, uris)
startService(serviceIntent);
It works for all file providers and usually for content as well, but from time to time I get reports of failures:
Non-fatal Exception: java.lang.SecurityException: UID 10283 does not have permission to content://com.whatsapp.provider.media/item/33560 [user 0]
Is there anything I can do to keep the Uri permission alive? how should I handle this kind of Uri?
please create xml folder in your app and in xml mentioned file provider file and network security configure file after that declare fileprovider in AndroidMenifest.xml file

Is it possible to reuse a content sheme URI

When opening a file from a file explorer I get a content scheme URI like following:
content://com.asus.filemanager.OpenFileProvider/file/sdcard/backups/apps/testfile.apk
I then make a temporary copy of the file using the content resolver using something like following:
File tempFile = getFileFromContentUri(getContext(), mUri, null);
After the app processed tempFile this file gets deleted. The problem is now that I want to forward mUri to another activity with following code, but I am getting a security exception while doing so, so it seems that the URI can only be used once, is this right?:
private forwardFile(Uri fileUri) {
final Intent installIntent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
Uri uri;
if (ContentResolver.SCHEME_CONTENT.equals(fileUri.getScheme())) {
uri = new Uri.Builder()
.path(fileUri.getPath())
.authority(fileUri.getAuthority())
.scheme(ContentResolver.SCHEME_CONTENT)
.build();
}
installIntent.setData(uri);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(installIntent);
}
Is there the possibility to reuse the uri or a workaorund that you can see? I'm not seeing a way I can handle this, e.g. I must delete the temp file, but if I forward the copied file instead of the original URI I don't get a callback so I wouldn't know when to delete the copied file.
And here the exception I am getting:
java.lang.SecurityException: Uid 10165 does not have permission to uri 0 # content://com.asus.filemanager.OpenFileProvider/file/sdcard/backups/apps/testfile.apk
Only if the serving app defines a persistent permission and the client calls context.getContentResolver().takePersistableUriPermission() this may work, but I am also not sure.
You got the permission for:
content://com.asus.filemanager.OpenFileProvider/file/sdcard/backups/apps/testfile.apk
No wonder that you got a java.lang.SecurityException: using a completely different (and non existing) file:
content://com.asus.filemanager.OpenFileProvider/file/sdcard/Download/testfile.apk

Categories

Resources