alexrintt / shared-storage

Flutter plugin to work with Android external storage.
http://alexrintt.io/shared-storage/
MIT License
53 stars 24 forks source link

ReadPermission False #106

Open Ranashaheryar opened 1 year ago

Ranashaheryar commented 1 year ago

Read Permission is false even after openDocumentTree but write permission is true

alexrintt commented 1 year ago

If you have write permission then automatically you have read permission, you don't need both.

Let me know if it answers your question! Thanks.

Stephen-Cronin commented 1 year ago

I came here with the same question, because it seems that although I do have write permission and can create files in the folder I selected, I can't read files.

I'm using the sample app, with the only changes being I specified 0.5.0 in pubspec.yaml and I added an extra action to try to read a file in granted_uri_card.dart, as follows (after the revoke action in _buildAvailableActions):

        DangerButton(
          'READ TEST',
          onTap: () {
            const String stringUri = 'content://com.android.externalstorage.documents/tree/primary:Sigames/Documents/Sports Interactive/Football Manager 2022 Mobile/normal/games/Sample File.txt';
            final Uri uri = Uri.parse(stringUri);
            String contents;
            contents = getDocumentContentAsString(uri).toString();
            print(contents);
          },
        ),

Note the URI string I'm using there is the one I got from output of the sample app after I select the folder (with the file name appended).

When I run the app, select the folder in question and grant permissions etc, the sample app shows that I have write permissions for that folder, but not read permissions, as seen here:

image

If I choose the Create Sample File action, it creates Sample Files.txt in the selected folder (or variants with the number at the end if that already exists). If I then choose the READ TEST action, I get the following error:

java.lang.SecurityException: Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri content://com.android.externalstorage.documents/tree/primary:Sigames/Documents/Sports%20Interactive/Football%20Manager%202022%20Mobile/normal/games/Sample%20File.txt from pid=23005, uid=10511 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs

I'm not that experienced with Flutter or Android, and I'm definitely brand new to SAF etc, so I may be missing something, but it looks to me as though I do indeed have write access, but not read access. The folder in question is owned by someone else's app, so maybe that's the problem? But surely if I can write to that folder I should be able to read the files I've written there.

Or is read permission only at the folder level, ie to be able to see the files in that folder, and I need to do something extra to actually read a file? Thanks.

alexrintt commented 1 year ago
const String stringUri = 'content://com.android.externalstorage.documents/tree/primary:Sigames/Documents/Sports Interactive/Football Manager 2022 Mobile/normal/games/Sample File.txt';

SAF needs to know the tree and the document, you're passing only the tree.

Take a look at this fn:

https://github.com/alexrintt/shared-storage/blob/500363b917304a454b393179b8dd3eb5386cbf5c/example/lib/screens/file_explorer/file_explorer_page.dart#L198

It does what you want: read the file tree within a folder.

Let me know if it helps.

Stephen-Cronin commented 1 year ago

Finally circling back to this. Thanks for the eg, but I still didn't really get the logic of it! I was trying to open a single file rather than read a file tree and just couldn't work out how to do it.

But then I noticed that there'd been a couple of new versions and that the example app has been updated, so I gave that a spin and it now has an Open document button/link. That does exactly what I want - although I'd love to be able to get access to all files in a folder rather than having to get the user to select them 1 by 1.

Anyway, I managed to edit the example to open a hex file and process the contents etc, so I've proved the concept to myself and gotten over the hurdle I was stuck at. Thanks so much for your help and the work on this library.

Stephen-Cronin commented 1 year ago

Well maybe I spoke too soon! I can get the user to give permission to access a file, then read it, but as soon as the file changes, the permission is gone again. Is that a limitation of the SAF model?

I need to be able to get permission to access a file once and then be able to access it through continuous changes. The context is I have an app that opens a save file from a third party game and shows the user extra information not available in the game itself. The user will be saving the save file constantly, but will then switch to my app and refresh it to see the changes. If I have to ask them for permission to open the file every time, that's going to drive them crazy...

I realise that this may very well be outside the scope of this library, but if you have any pointers (even it if is just confirming there's no way to do what I want through SAF), that would be a great help. Thanks

alexrintt commented 1 year ago

The context is I have an app that opens a save file from a third party game and shows the user extra information not available in the game itself.

I think this is supported with openDocument method, and you don't need to ask it every time. Can you show your code?

Also, can you provide your Android version?


Side-note: this use-case needs the library v0.7.0, since this release launched the openDocument method.

alexrintt commented 1 year ago

so I gave that a spin and it now has an Open document button/link. That does exactly what I want - although I'd love to be able to get access to all files in a folder rather than having to get the user to select them 1 by 1.

That's the purpose of openDocumentTree, I think you can achieve it by using this function.


Edit: managed to use openDocument flawlessly, tested on Android 8 and 13, same for openDocumentTree. I'll need a reproducible example or a piece of code to help you. Let me know when you have more details.

Stephen-Cronin commented 1 year ago

@alexrintt Thanks for your quick response.

Also, can you provide your Android version?

This is happening when running debug on a Samsung S20+ with Android 12.


I think this is supported with openDocument method, and you don't need to ask it every time. Can you show your code?

I've investigated this a little further now and have some more details.

This is using the example app from the latest .zip file download. My only changes were to specify 0.7.0 in pubspec.yaml and then a few changes to ShowDocumentFileContents in the lib\utils\document_file_utils.dart file as follows:

Here's a screenshot of the changes: image

Apart from that the code is exactly the same as the demo.

I then used the New allowed files link in the app and selected the file I wanted to open, resulting in this:

When I click Open document, it reads the file and displays the text seen in the following screenshot (date, name, teams) - I'll be converting the entire file content to HEX, but it's quite useful that it's displaying the string value here:

So far so good. Even better, if I use a HEX editor to change the contents of the file and save it and then click Open document again, the changes appear! At one point during my testing, I deleted the file from the phone entirely, then copied a modified version back and it had no problem opening it.

However, if I go back to the third party game and use the save game function in there and then click Open document again, the app crashes and I see these in the debug console:

I/MSHandlerLifeCycle(12554): isMultiSplitHandlerRequested: windowingMode=1 isFullscreen=true isPopOver=false isHidden=false skipActivityType=false isHandlerType=true this: DecorView@bc8af9d[MainActivity]

W/DocumentFile(12554): Failed query: java.lang.SecurityException: Permission Denial: opening provider [com.android.externalstorage.ExternalStorageProvider](http://com.android.externalstorage.externalstorageprovider/) from ProcessRecord{e24af6b 12554:io.alexrintt.sharedstorage.example/u0a529} (pid=12554, uid=10529) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs

E/AndroidRuntime(12554): FATAL EXCEPTION: DefaultDispatcher-worker-1

E/AndroidRuntime(12554): Process: io.alexrintt.sharedstorage.example, PID: 12554

E/AndroidRuntime(12554): java.lang.SecurityException: Permission Denial: opening provider [com.android.externalstorage.ExternalStorageProvider](http://com.android.externalstorage.externalstorageprovider/) from ProcessRecord{e24af6b 12554:io.alexrintt.sharedstorage.example/u0a529} (pid=12554, uid=10529) requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs

E/AndroidRuntime(12554):    at android.os.Parcel.createExceptionOrNull([Parcel.java:2438](http://parcel.java:2438/))

When I restart the app (via VS Code Run and Debug), the permission for that file no longer appears (it is normally retained when disconnecting and reconnecting/re-running the app). I can of course open New allowed files and select the file again and it will get the contents of the modified file, but that's what I'm hoping to avoid.

So it looks like I DO get persistent permissions to the file that last beyond file changes in most cases, but NOT when the original app that owns that file changes it. At that point permissions are ... revoked? Maybe it's something they do with saving the file? Maybe it's because that app own's the file?

I have no idea about what could be causing that and I appreciate you won't be able to replicate it without having the game, so not sure where to from here...


With the OpenDocumentTree approach (getting permissions for the folder then opening files from within it), that's what I was trying to do with the older version, but I was probably missing something. Maybe I need to run OpenDocumentTree first and then run OpenDocument? Anyway, I guess that's a secondary issue to the one above so I'll forget it for now.

Thanks again for your help with this, I really appreciate it.

alexrintt commented 1 year ago

Can you show the full error stack trace? I wanna see the path of the native function calls, but you uploaded only few lines. You can create a gist and then paste the link here if you want to avoid spamming 300 lines of log.

I could not get to reproduce this error in any type of environment, even between several application. I've tested already with Android 8, 10, 12 and 13. Would be helpful if you share your game (where I can download). If you wanna share the game name in private, you can always send me a message on telegram or at my email reach@alexrintt.io.

Stephen-Cronin commented 1 year ago

Here is the full content of the debug console covering launch app in debug mode, select file, open file successfully, switch to third party game and open / resave file there, switch back to app and choose Open file again:

debug-console.txt

I'll email you some details about the third-party game (just to be clear, I'm not the author of the game, I'm making a tool that works with someone else's game, if that makes any difference). Thanks again for your help.