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
Related
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.
So I have an android app which opens and displays PDF's, I have the user select pdfs like this
fun openFile() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "application/pdf"
putExtra(Intent.EXTRA_MIME_TYPES, arrayOf("application/pdf"))
putExtra(DocumentsContract.EXTRA_INITIAL_URI, MediaStore.Files.getContentUri("external"))
}
activity?.startActivityForResult(intent, REQUEST_CODE_PDF_FILE)
}
And then I retrieve the URI from the activity result display it and save the URI. However the next time the app is opened I want to be open that same file, right now when I try opening the saved URI I get the following:
ava.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{c3dfcb2 32587:ca.thing.testapp/u0a237} (pid=32587, uid=10237) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
at android.os.Parcel.createExceptionOrNull(Parcel.java:2425)
at android.os.Parcel.createException(Parcel.java:2409)
at android.os.Parcel.readException(Parcel.java:2392)
at android.os.Parcel.readException(Parcel.java:2334)
at android.app.IActivityManager$Stub$Proxy.getContentProvider(IActivityManager.java:5850)
at android.app.ActivityThread.acquireProvider(ActivityThread.java:6973)
So clearly after closing and reopening the app I no longer have permission to use that selected file. So I imagine what I need to do is make a copy of that file into some cache dir that I do have permissions in so that I can display it when the app is reopened. How would I go about doing that?
You should take persistable uri permission in onActivityResult in order to use the uri later.
Making a copy is not needed.
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
I got a Uri using the following method.
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, MY_REQUEST_CODE);
likeļ¼
content://com.android.externalstorage.documents/tree/primary%3A
Can I store this string to create a DocumentFile in application startup?
like:
DocumentFile.fromUri(this,Uri.pasre(str))
It works on Android 6.0,but it seems invalid in higher version.
Or I should get Uri every time I start it?
I have found that some file managers do not need to get Uri via startActivityForResult(). Like RE. Are they using the shell to get a list under /storage and then stitching them into Uri?
Then use grantUriPermission() and takePersistableUriPermission() to get Uri permissions?
Sorry, my English is too bad. TT
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