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

[Bug] FilePicker stopped working on Android #1538

Closed
IngweLand opened this issue Nov 19, 2020 · 19 comments · Fixed by #1555
Closed

[Bug] FilePicker stopped working on Android #1538

IngweLand opened this issue Nov 19, 2020 · 19 comments · Fixed by #1555
Labels
bug Something isn't working
Milestone

Comments

@IngweLand
Copy link

IngweLand commented Nov 19, 2020

Description

Selecting any file on Android does not work.

Steps to Reproduce

  1. Start selecting the file. File picker will open
  2. Pick the file which resides on the phone (to be sure that its binary payload is indeed available for the app). await fileData.OpenReadAsync()
  3. Notice the exception
Java.Lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/video%3A28 from pid=10506, uid=10265 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
  at Java.Interop.JniEnvironment+InstanceMethods.CallNonvirtualObjectMethod (Java.Interop.JniObjectReference instance, Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x0008e] in <89755ea61d9c4ae0a40ce90b872c9e2d>:0 
  at Java.Interop.JniPeerMembers+JniInstanceMethods.InvokeNonvirtualObjectMethod (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters) [0x0001f] in <89755ea61d9c4ae0a40ce90b872c9e2d>:0 
  at Android.Content.ContentResolver.OpenInputStream (Android.Net.Uri uri) [0x00031] in <4658d344ef4b4a429f9058200bb1b31c>:0 
  at Xamarin.Essentials.FileBase.PlatformOpenReadAsync () [0x0001e] in D:\a\1\s\Xamarin.Essentials\FileSystem\FileSystem.android.cs:119 
  at Xamarin.Essentials.FileBase.OpenReadAsync () [0x00000] in D:\a\1\s\Xamarin.Essentials\FileSystem\FileSystem.shared.cs:105 
  at MyClass.uploadFile (Xamarin.Essentials.FileResult fileData) [0x001ee] in D:\MyClass.cs:552 
  --- End of managed Java.Lang.SecurityException stack trace ---
java.lang.SecurityException: Permission Denial: reading com.android.providers.media.MediaDocumentsProvider uri content://com.android.providers.media.documents/document/video%3A28 from pid=10506, uid=10265 requires that you obtain access using ACTION_OPEN_DOCUMENT or related APIs
	at android.os.Parcel.createExceptionOrNull(Parcel.java:2373)
	at android.os.Parcel.createException(Parcel.java:2357)
	at android.os.Parcel.readException(Parcel.java:2340)
	at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:190)
	at android.database.DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(DatabaseUtils.java:153)
	at android.content.ContentProviderProxy.openTypedAssetFile(ContentProviderNative.java:781)
	at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1983)
	at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:1798)
	at android.content.ContentResolver.openInputStream(ContentResolver.java:1475)
	at mono.java.lang.RunnableImplementor.n_run(Native Method)
	at mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:30)
	at android.os.Handler.handleCallback(Handler.java:938)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loop(Looper.java:223)
	at android.app.ActivityThread.main(ActivityThread.java:7656)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

Expected Behavior

Should be able to pick the file

Actual Behavior

Cannot pick the file

Basic Information

  • Version with issue: 1.6.0-pre4

  • Last known good version: 1.6.0-pre3

  • IDE: VS 2019 16.8.0

  • Platform Target Frameworks:

    • Android: 11
  • Affected Devices: Pixel3a, Android 11

Workaround

Downgrade to 1.6.0-pre3

@IngweLand IngweLand added the bug Something isn't working label Nov 19, 2020
@mattleibow
Copy link
Contributor

This might have been introduced here: #1446 @dimonovdd
This was to fix: #1444

@IngweLand what file is this? A local file? A virtual (on google drive) file?

I don't remember getting the error at all, so this is something new.

@mattleibow mattleibow added this to the 1.6.0 milestone Nov 19, 2020
@IngweLand
Copy link
Author

IngweLand commented Nov 19, 2020

I also have the feeling that it was introduced in #1446
I tried with different files, it stopped working completely. I tried picking same files with pre4 and pre3. Pre3 works. I have just default vanilla file picker, no 3rd party apps installed.

@IngweLand
Copy link
Author

OK, it seems that the issue is project specific. I have just created a minimal sample app with latest Essentials, and can select the file on that same device. Will investigate, what's so specific with my app why it stopped working there.

@IngweLand
Copy link
Author

Here is the simple code to reproduce the issue. The reason is in that delay. Everything works when it's extremally short (<50 millis). However, it starts failing when the delay is >100 millis. In my app, I have to introduce that delay purely as a hack, so the loading bar have time to appear (selecting big files blocks the UI completely, even though it has an async API).

private async void Button_OnClicked(object sender, EventArgs e)
        {
            FileResult fileData = null;
            try
            {
                fileData = await FilePicker.PickAsync();
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"<||> {ex}");
                return;
            }

            if (fileData != null)
            {
                try
                {
                    await Task.Delay(100);
                    var stream = await fileData.OpenReadAsync();
                    Debug.WriteLine($"<||> {stream?.Length}");
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"<||> {ex}");
                }
            }
            else
            {
                Debug.WriteLine($"Got null file data when picking the file");
            }
        }

@IngweLand
Copy link
Author

I have managed to restructure my app and got it working. Not sure if this still should be considered a bug or not. It does not feel right that the file access got invalid just in 100 milliseconds. Although, this might be expected.

@dimonovdd
Copy link
Contributor

dimonovdd commented Nov 20, 2020

@IngweLand @mattleibow

It does not feel right that the file access got invalid just in 100 milliseconds.

I tested this with delay(1000), and it works correctly

@mattleibow
Copy link
Contributor

I was thinking that maybe it might not work with the fact we are no longer using persisted uris, but it only resets after a reboot.

@IngweLand, are you able to test on another device or even another emulator? I tested on a Pixel 2 and a Razer Phone without issues.

@IngweLand
Copy link
Author

I will try to test on another device/emulator.

Currently, my setup is:
Target SDK - Android 11
Device: Google Pixel 3a
Device OS: Android 11

The workaround, which worked for me, was to get the reference to that stream right away, and then use the delay for a UI hack.

@mattleibow
Copy link
Contributor

This is interesting as the fact that the item is in the manifest means that it should keep the code. The manifest doesn't get linked, then the manifest references the class, which preserves it.

Maybe something changed in the linker that now links first before adding it to the manifest... I'll check with the Android team.

@IngweLand
Copy link
Author

Checked with another device, and it worked with delay there:

Target SDK - Android 11
Device: some old 4-5 years Lenovo
Device OS: Android 6 (API 23)

@mattleibow
Copy link
Contributor

@dimonovdd what device were you testing on? I hope this is not an issue with a specific device...

@dimonovdd
Copy link
Contributor

@mattleibow
xiaomi redmi 9 (android 10)
simulator (android 10 and 11)

@mattleibow
Copy link
Contributor

Maybe this is due to some GC operation collecting something which in turn invalidates a file?

Try adding a GC.Collect() before reading it.

@mattleibow
Copy link
Contributor

@mattleibow
Copy link
Contributor

Based on that post, it looks like it is getting cleanup up.

@IngweLand if you open a stream, and then delay for a long time, does it still work? We might be able to do the same trick we do with iOS and open the stream, and then pass that to the result object:

https://github.com/xamarin/Essentials/blob/main/Xamarin.Essentials/FileSystem/FileSystem.ios.cs#L17-L26

@dimonovdd
Copy link
Contributor

@IngweLand can you submit a sample project that reproduce this issue?
I would like to help with solving this issue. this feature is very important to me

@IngweLand
Copy link
Author

The sample project would be very basic:

  1. Create default Xamarin.Forms project
  2. Add Essentials nuget and upgrade all other nugets
  3. Put one button on the screen.
  4. Add Click handler
  5. Test on Android 11 device (I am not sure if this issue exists on Emulator).
private async void Button_OnClicked(object sender, EventArgs e)
        {
            FileResult fileData = null;
            try
            {
                fileData = await FilePicker.PickAsync();
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"<||> {ex}");
                return;
            }

            if (fileData != null)
            {
                try
                {
                    await Task.Delay(100);
                    var stream = await fileData.OpenReadAsync();
                    Debug.WriteLine($"<||> {stream?.Length}");
                }
                catch (Exception ex)
                {
                    Debug.WriteLine($"<||> {ex}");
                }
            }
            else
            {
                Debug.WriteLine($"Got null file data when picking the file");
            }
        }

On a side note. While my concrete use case (with that delay for UI hack) is not a common one, I can imagine more valid general scenario.

  1. User picks the file
  2. User have to do something else in the UI before that file will be used. E.g., there might be a text input for new file name. This action will introduce the delay.
  3. The app calls fileData.OpenReadAsync();

@mattleibow
Copy link
Contributor

I think I know what is happening. We use an intermediate activity for launching the picker. This makes sure that you don't have to worry about callbacks. However, it turns out that once the intermediate activity finishes, then the file that was picked is cleaned up. This is why when you open it really quickly, it works, but the delay fails.

I am looking at what we can do, and I think I have an idea that I am trying out now.

@mattleibow
Copy link
Contributor

mattleibow commented Nov 29, 2020

Does this happen with the MediaPicker? Based on my understanding, it should not since it uses a different intent action.

I answered my own question. This also happens with the MediaPicker.

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