Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do I need to configure anything in AndroidManifest.xml? #117

Closed
shinexoh opened this issue Mar 19, 2023 · 8 comments
Closed

Do I need to configure anything in AndroidManifest.xml? #117

shinexoh opened this issue Mar 19, 2023 · 8 comments

Comments

@shinexoh
Copy link

I want to use the openDocumentTree method to authorize the Android/data/somedirectory/ Directory
Then use the getDocumentContent 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 the openDocument 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 the openDocument 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

@alexrintt
Copy link
Owner

alexrintt commented Mar 19, 2023

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/.

@shinexoh
Copy link
Author

shinexoh commented Mar 19, 2023

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

@alexrintt
Copy link
Owner

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:

  • When you manually select the file, your code works because the returned Uri is containing the tree path plus the document uri, as it should be.
  • When you select the tree, you are trying to read the document path without the tree path, AFAIK this leads to 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);
}

@shinexoh
Copy link
Author

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?

@alexrintt
Copy link
Owner

TD;DR: Follow these steps:

  1. Use the following template:
content://com.android.externalstorage.documents/tree/{encodedTreeUri}/document/{encodedSubfileUri}
  1. Replace 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 /).
  2. Replace 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 /).
  3. The final result is the subfile (aka child file) URI.

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.

@shinexoh
Copy link
Author

thanks

@shinexoh
Copy link
Author

shinexoh commented Apr 3, 2023

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

@alexrintt
Copy link
Owner

Do not re-use closed issues, open a new one.

Repository owner locked as resolved and limited conversation to collaborators Apr 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants