Selecting any file on Android does not work.
await fileData.OpenReadAsync()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)
Should be able to pick the file
Cannot pick the file
Platform Target Frameworks:
Affected Devices: Pixel3a, Android 11
Downgrade to 1.6.0-pre3
This might have been introduced here: https://github.com/xamarin/Essentials/pull/1446 @dimonovdd
This was to fix: https://github.com/xamarin/Essentials/issues/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.
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.
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.
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");
}
}
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.
@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
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.
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.
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.
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)
@dimonovdd what device were you testing on? I hope this is not an issue with a specific device...
@mattleibow
xiaomi redmi 9 (android 10)
simulator (android 10 and 11)
Maybe this is due to some GC operation collecting something which in turn invalidates a file?
Try adding a GC.Collect() before reading it.
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
@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
The sample project would be very basic:
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.
fileData.OpenReadAsync();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.
~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.
Most helpful comment
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.