Closed shinexoh closed 1 year ago
So I want to make sure that my application won't invalidate my file permissions when other applications edit the file.
This is the default behavior, once the user grant permission over a Uri, you have permission over all subfiles (recursively), so even if other app edit a file that is in under your granted tree, you still have access to it, also applies for created files, what you want to achieve is what is supposed to happen.
I need some code and details to know what is exactly happening in your case, can you show how you are using and what is your target Android version?
A side-note: Android/data
folder is not permitted to be selected anymore (even if the user wants!) on higher Android versions (12+), so be aware of this limitation (same applies for MANAGE_EXTERNAL_STORAGE
permission) see https://www.reddit.com/r/Android/comments/wru35i/clearing_up_confusion_about_how_to_access/.
Android does not allow you to select Android/data/, but you can select a subdirectory within it. Just set the following Uri for the initialUri
, and I can successfully select android/data/mark.via/
content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata%2Fmark.via
This is the variable I declare for the directory Uri
final dUri = Uri.parse(
'content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata%2Fmark.via');
This is to obtain the Uri variable of the "app. ini" file in the directory
final fUri = Uri.parse(
'content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fdata%2Fmark.via%2Ffiles%2Flogs%2Fapp.ini');
This is the method to execute the authorization directory:
void _get() async {
final Uri? grantedUri = await openDocumentTree(
initialUri: dUri,
);
}
This is the string execution method for viewing files:
void _read() async {
try {
final readFile = await getDocumentContent(fUri);
print(String.fromCharCodes(readFile!));
} catch (e) {
print(e);
}
}
This is my method for authorizing sub files (click to manually select the file)
void _getFile() async {
final file = await openDocument(
initialUri: dUri,
);
}
After executing the _get
method to authorize the directory, I successfully obtained permissions for the directory
However, when I execute _read
to attempt to view content, I am still not allowed to view it (directly exiting the application crashes), so I must execute the _getFile
method to manually select files to obtain authorization. only in this way can I successfully execute_ read
Other applications edit sub files in the authorized directory, and my permissions will disappear (I mean _getFile
authorization sub file permissions). The persistedUriPermissions
method will reduce the length once from the original length, as well as in the phone settings.
My device is Android 13 targetSdkVersion 29
TL;DR: Try declaring the tree owner of your document at fUri
.
final fUri = Uri.parse(
- 'content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fdata%2Fmark.via%2Ffiles%2Flogs%2Fapp.ini'
+ 'content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata%2Fmark.via/document/primary%3AAndroid%2Fdata%2Fmark.via%2Ffiles%2Flogs%2Fapp.ini'
);
What I could understand from your functions is that:
SecurityException
.So here is an example that creates your dUri
and fUri
correctly and can possible fix your issue:
void main() {
// Granted tree uri:
final dUri = Uri(
scheme: 'content',
host: 'com.android.externalstorage.documents',
path: '/tree/primary%3AAndroid%2Fdata%2Fmark.via',
);
// content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata%2Fmark.via
print(dUri);
// Sub-file uri:
final fUri = dUri.replace(
path: dUri.path +
'/document/primary%3AAndroid%2Fdata%2Fmark.via%2Ffiles%2Flogs%2Fapp.ini',
);
// content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata%2Fmark.via/document/primary%3AAndroid%2Fdata%2Fmark.via%2Ffiles%2Flogs%2Fapp.ini
print(fUri);
}
Thank you. Now that the problem has been resolved, I have been using the Uri returned by the openDocument
selection sub file as a parameter until the problem has been resolved. Now I wonder how you got this new Uri? Because in the future, I will have to select other sub files. How do I generate this Uri?
TD;DR: Follow these steps:
content://com.android.externalstorage.documents/tree/{encodedTreeUri}/document/{encodedSubfileUri}
encodedTreeUri
with the URL encoded version of primary:{path-to-your-desired-storage-tree}
, in your case: primary:Android/data/mark.via
(The path must not start with a /
).encodedSubfileUri
with the URL encoded version of primary:{path-to-your-desired-storage-file}
, in your case: primary:Android/data/mark.via/files/logs/app.ini
(The path must not start with a /
).Dart implementation:
final Uri dUri = createTreeUri('Android/data/mark.via');
final Uri fUri =
createDocumentUri(dUri, 'Android/data/mark.via/files/logs/app.ini');
Uri createTreeUri(
String treePath, {
String host = 'com.android.externalstorage.documents',
String scheme = 'content',
String storage = 'primary',
}) {
assert(
!treePath.startsWith('/'),
'Please, remove `/` from the start of your [treePath].',
);
final Uri treeUri = Uri(
host: host,
scheme: scheme,
path: '/tree/${Uri.encodeComponent('$storage:$treePath')}',
);
return treeUri;
}
Uri createDocumentUri(
Uri treeUri,
String documentPath, {
String storage = 'primary',
}) {
assert(
!documentPath.startsWith('/'),
'Please, remove `/` from the start of your [documentPath].',
);
final Uri documentUri = treeUri.replace(
path:
'${treeUri.path}/document/${Uri.encodeComponent('$storage:$documentPath')}',
);
return documentUri;
}
I must warn that this is specific for your use-case: generating a pre-defined Uri from a pre-defined path that will be in the primary storage of the device using the default android storage provider. This will not work for all use-cases, like generating a URI for a cloud storage provider or even a custom storage provider. This also may not work for some devices.
thanks
Bro, I have encountered a problem again and I want to seek your help. I successfully granted the directory using OpenDocumentTree, and I also used the sub file Uri you generated for me before. I can use the Uri of the sub file to operate on it successfully, but it only works for a short period of time (it may be because I restarted my phone because I am using the AS virtual machine). Afterwards, whenever I call the method to operate on the sub file, I will be prompted:
W/DocumentFile(13899): Failed query: java.lang.SecurityException: Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata%2Fmark.via%2Fdemo/document/primary%3AAndroid%2Fdata%2Fmark.via%2Fdemo/children from pid=13899, uid=10166 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
8
W/DocumentFile(13899): Failed query: java.lang.SecurityException: Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri content://com.android.externalstorage.documents/tree/primary%3AAndroid%2Fdata%2Fmark.via%2Fdemo/document/primary%3AAndroid%2Fdata%2Fmark.via%2Fdemo%2FUserCustom.ini%20(4) from pid=13899, uid=10166 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
I have already set up OpenDocumentTree
grantWritePermission = true
persistablePermission = true
And the data returned by my call to persistedUriPermissions
also includes the directory Uri I authorized, I don't understand why this is happening. My current solution is to grant directory permissions again whenever there is an error, but this does not solve the root cause of the problem
Do not re-use closed issues, open a new one.
I want to use the
openDocumentTree
method to authorize theAndroid/data/somedirectory/
Directory Then use thegetDocumentContent
method to read or write sub files in the directory, so I don't need to authorize the files I want to read and write again through theopenDocument
method.Because my current situation is that if I edit the file using another app (an app of the file management editing type), the sub file permissions that I authorize using the
openDocument
method within my application will become invalid, and then I need to call theopenDocument
method again to let the user select the file to regain read and write permissions.So I want to make sure that my application won't invalidate my file permissions when other applications edit the file. Do I need to configure something in AndroidManifest.xml? How can I achieve this