Skip to content
This repository has been archived by the owner on May 15, 2024. It is now read-only.

[Bug][Android] File Picker exception when using third-party file managers #1444

Closed
dimonovdd opened this issue Oct 12, 2020 · 19 comments · Fixed by #1446
Closed

[Bug][Android] File Picker exception when using third-party file managers #1444

dimonovdd opened this issue Oct 12, 2020 · 19 comments · Fixed by #1446
Labels
bug Something isn't working
Milestone

Comments

@dimonovdd
Copy link
Contributor

dimonovdd commented Oct 12, 2020

Description

An exception occurs when using third-party file managers.

Selecting files on WhatsApp app using this and other third-party file managers on phones that I tested works without errors.

Exception StackTrace

Steps to Reproduce

  1. Install File Manager+
  2. Select File Manager+

Basic Information

  • Affected Devices: tested on several Androids 10

Screenshots

image

@vividos
Copy link
Contributor

vividos commented Oct 14, 2020

I think the bug is that TakePersistableUriPermission() is called without checking if the content provider granted permissions:
https://developer.android.com/reference/android/content/ContentResolver#takePersistableUriPermission(android.net.Uri,%20int)

The code should probably look like this:

if ((data.Flags & Intent.GrantReadUriPermission) != 0)
    Platform.AppContext.ContentResolver.TakePersistableUriPermission(contentUri, ActivityFlags.GrantReadUriPermission);

(or some variant of it).

@dimonovdd
Copy link
Contributor Author

dimonovdd commented Oct 14, 2020

@vividos
No, When I use a standard file Manager (Files on the screenshot), there are no errors

@vividos
Copy link
Contributor

vividos commented Oct 14, 2020

I meant the case when FileManager+ is used. Could you try if the if in front of the call to TakePersistableUriPermission helps? Thanks!

@dimonovdd
Copy link
Contributor Author

dimonovdd commented Oct 14, 2020

@vividos Your example is incorrect

I tried it. There is no error and I get the file.

if (intent.Flags == ActivityFlags.GrantReadUriPermission)
{
   Platform.AppContext.ContentResolver.TakePersistableUriPermission(
   contentUri,
   ActivityFlags.GrantReadUriPermission);
}

But the result of this if is always false, because of this:
intent.AddFlags(ActivityFlags.GrantPersistableUriPermission);

@vividos
Copy link
Contributor

vividos commented Oct 14, 2020

Let me check that when I'm at home. My guess is that the FileManager doesn't grant the permission, and then the FilePicker can't take the permission.

@dimonovdd
Copy link
Contributor Author

@vividos I tested this pull request on 5 devices with Android 10, and it works well

@dimonovdd
Copy link
Contributor Author

@mattleibow @jamesmontemagno @Redth
What do you think?

@vividos
Copy link
Contributor

vividos commented Oct 15, 2020

if (intent.Flags == ActivityFlags.GrantReadUriPermission)

I meant checking the result intent if the flag has been set, not the original intent:

if (result.Flags.HasFlag(ActivityFlags.GrantReadUriPermission))

Unfortunately, while reading about TakePersistableUriPermission I found another possible issue calling that method: One can only take 128 persistable permissions before running into a limit: https://commonsware.com/blog/2020/06/13/count-your-saf-uri-permission-grants.html

@vividos
Copy link
Contributor

vividos commented Oct 15, 2020

I tried out file picking using an Android 10 emulator and the File Manager Plus app installed. The app returns a result intent that doesn't have set the ActivityFlags.GrantPersistableUriPermission flag, whereas the "Files" picker does. So the correct if statement to wrap the TakePersistableUriPermission would be:

if (result.Flags.HasFlag(ActivityFlags.GrantPersistableUriPermission))

I'm also interested what the Essentials devs think!

@dimonovdd
Copy link
Contributor Author

@vividos
I think that if is not the best solution for this issue
The developer will not be notified in any way that the behaviour of the method has changed.

I think it's better to delete this code at all

Platform.AppContext.ContentResolver.TakePersistableUriPermission(contentUri, ActivityFlags.GrantReadUriPermission);

@vividos
Copy link
Contributor

vividos commented Oct 15, 2020

Yes, maybe it's best, but then the Uri is not persisted between reboots and that may also be expected of the user of the library. And when you read the commonsware.com link, we would get another problem if more than 128 Uris are persisted per app, which may be unusual, but possible.

@dimonovdd
Copy link
Contributor Author

dimonovdd commented Oct 15, 2020

but then the Uri is not persisted between reboots

But this will be the same for different file managers

128 Uris are persisted per app

How can we clear them?

@vividos
Copy link
Contributor

vividos commented Oct 16, 2020

Please read the mentioned article, it's all in there.

@dimonovdd
Copy link
Contributor Author

I expressed myself wrong
How to clear the saved Uris on Android via the Essentials Api?
After all, then the implementation of Android will be very different from other platforms

@dimonovdd
Copy link
Contributor Author

@jamesmontemagno @Redth @mattleibow @vividos
I think it's better:

  1. delete this code at all
Platform.AppContext.ContentResolver.TakePersistableUriPermission(contentUri, ActivityFlags.GrantReadUriPermission);
  1. dotnt use MANAGE_DOCUMENTS permission

  2. add this info to the documentation
    On Android a Uri may not be saved between reboots

@vividos
Copy link
Contributor

vividos commented Oct 20, 2020

See this PR on the original FilePicker on why the call to TakePersistableUriPermission() was added in the first place:
jfversluis/FilePicker-Plugin-for-Xamarin-and-Windows#178

@mattleibow
Copy link
Contributor

HI folks, I am looking at this and I am a bit unsure of exactly what needs to be done. I see we have set the ActivityFlags.GrantPersistableUriPermission flag, but do we need to? This is just for the time the FileResult lives, and it can't really be used in shared code with the content://. I am not even sure why we expose that version.

But, basically I see two sides:

  • use the flag and get a persistent uri, but we have no way to clear it
  • don't use the flag, but nobody can save the content:// uri

However, since the content:// uri is very much dependent on the device state - for example if the image comes from an app, but then the user uninstalls that app - so maybe we don't need the persist flag?

@mattleibow mattleibow added this to the 1.6.0 milestone Nov 5, 2020
@vividos
Copy link
Contributor

vividos commented Nov 6, 2020

In the past, in the FilePicker plugin, we had developers that grabbed the content Uri and stored it somewhere, expecting to be some kind of path that they can access indefinitely. So this is mostly a documentation issue. I would suggest to remove the persistent Uri stuff and add documentation that the FileResult is temporary and that files need to be copied to the app data folder or read immediately. I even would show some code using Stream.CopyToAsync on how to do it.

@dimonovdd
Copy link
Contributor Author

dimonovdd commented Nov 6, 2020

I think this is the right way
#1444 (comment)

You should also not forget the cleaning when using the ActivityFlags.GrantReadUriPermission
#1444 (comment)

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants