Can I create a DocumentFile with a stored Uri? - android

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

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

Can't figure out how to use ACTION_VIEW and Storage Access Framework together

After about a week of pulling my hair out, I'm finally done and ready to ask for some help.
Basically in my app I use the Intent below to create a new PDF, which is done via Storage Access Framework.
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "application/pdf"
intent.putExtra(Intent.EXTRA_TITLE, title)
startActivityForResult(intent, 1234)
After that I get the Uri on the onActivityResult() method, like so:
uri = dataIntent.data
if (uri != null) {
val takeFlags = data.flags and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
contentResolver.takePersistableUriPermission(uri, takeFlags)
generatePdf(uri)
}
PDF generation is ok, the problem comes when I need to call ACTION_VIEW for the user to see the generated file or to share the file using ACTION_SEND.
Example of ACTION_VIEW usage (Yes, I'm using both Kotlin and Java):
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(uri, mimeType);
startActivity(intent);
I can't for the life of me figure out how to get an Uri that another app can use.
What I tried so far:
This answer, but the following exception is thrown: java.lang.IllegalArgumentException: column '_data' does not exist. Available columns: [_display_name, _size]
DocumentFile, using DocumentFile.fromFile(file), which turns the Uri from content://com.myapp.provider/root/document/primary:folder-created-by-the-user/generated-pdf.pdf to file:///root/document/primary:folder-created-by-the-user/generated-pdf.pdf, and still no app can open it
Many many other things that I can't even remember anymore
If someone could shed some light on this issue would be truly appreciated.
In principle use the same uri as obtained at creating the file. But ...you cannot grant a read uri permission on that uri. You got it. But you cannot forward such a permission to a viewer of your document.
Instead you should implement a ContentProvider. Then you can serve the content of your file.
Like blackapps said in his response, what I had to do was implement a ContentProvider, more specifically a DocumentProvider.
Following this link and this link is what finally did the trick. I implemented a CustomDocumentProvider that exposes a folder inside my app's private files (context.getFilesDir().getAbsolutePath() + "/folderToExpose"), after that all files created in this folder were exposed to other apps and I could use ACTION_VIEW and ACTION_SEND normally.
If someone happens to come across this issue, just make sure that the folder you want to expose doesn't contain any files that are crucial to your app, like database files, since users will have full access to all of its contents. And if it is a new folder, make sure to create it by calling mkdirs().

is the Uri returned by Intent.ACTION_GET_CONTENT always somewhere on disk, or can it be anywhere?

Currently I have the following code that allows a user to choose an image.
int requestCode = 1337;
Intent chooserIntent = new Intent(Intent.ACTION_GET_CONTENT);
chooserIntent.setType("image/*");
chooserIntent = Intent.createChooser(chooserIntent, "Please choose a picture");
chooserIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivityForResult(chooserIntent, requestCode);
My question is:
does Android guarantee that the returned Uri is always pointing to a location on disk, or is it possible that it might be pointing to somewhere on the internet too?
P.S. although I am not sure about this, the Uri returned by this piece of code seems to always start with content:// - I am not sure whether or not this holds for all possible return values, I thought I would just add this here to help out any possible question answerers.
does Android guarantee that the returned Uri is always pointing to a
location on disk, or is it possible that it might be pointing to
somewhere on the internet too?
It is possible to have Uri other than local disk i.e. it can be remotely as well. You will get URL from remote then convert it to Uri and use it.
From official docs:
An ACTION_GET_CONTENT could allow the user to create the data as it
runs (for example taking a picture or recording a sound), let them
browse over the web and download the desired data, etc.
Convert Url to a Uri (Reference):
Uri uri = Uri.parse( "http://www.facebook.com" );

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