Essentials
Essentials copied to clipboard
[Bug] Share.RequestAsync causes java.lang.SecurityException: Permission Denial
Description
Using Share.RequestAsync
with ShareFileRequest
prints out an exception on the Debug console (actual URI has been replaced with <redacted>
):
06-21 16:57:58.952 E/DatabaseUtils( 8291): Writing exception to parcel
06-21 16:57:58.952 E/DatabaseUtils( 8291): java.lang.SecurityException: Permission Denial: reading xamarin.essentials.fileProvider uri content://<redacted> from pid=8356, uid=1000 requires the provider be exported, or grantUriPermission()
06-21 16:57:58.952 E/DatabaseUtils( 8291): at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:729)
06-21 16:57:58.952 E/DatabaseUtils( 8291): at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:602)
06-21 16:57:58.952 E/DatabaseUtils( 8291): at android.content.ContentProvider$Transport.query(ContentProvider.java:231)
06-21 16:57:58.952 E/DatabaseUtils( 8291): at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:104)
06-21 16:57:58.952 E/DatabaseUtils( 8291): at android.os.Binder.execTransactInternal(Binder.java:1021)
06-21 16:57:58.952 E/DatabaseUtils( 8291): at android.os.Binder.execTransact(Binder.java:994)
Despite the exception printed, the app works as expected.
Steps to Reproduce
- Create a file in
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
. - Call
await Share.RequestAsync(new ShareFileRequest(new ShareFile(path)));
Expected Behavior
Shows the platform-dependent share UI to share the file. No exception printed on the console.
Actual Behavior
Prints an exception to the console. The share UI is shown as expected.
Basic Information
- Version with issue: 1.5.3.2
- Last known good version: Unknown
- IDE: Visual Studio Community 2019
- Platform Target Frameworks:
- iOS: None
- Android: 10.0 (API 29)
- UWP: None
- Android Support Library Version:
- Nuget Packages: Acr.UserDialogs, AiForms.SettingsView, CsvHelper, DryIoc.dll, MetadataExtractor, MvvmLightLibsStd10, NLog, Resizetizer.NT, SkiaSharp.Views.Forms, sqlite-net-pcl
- Affected Devices: Google Pixel 2
@redcurry I'm guessing that is caused by the change on Android 10 permissions. If you target Android9.0 this error occurs?
Are you missing a perrmision granted?
String packageName = resolveInfo.activityInfo.packageName; context.grantUriPermission(some permissions);
@pictos I targeted Android 9.0 and the exception did not happen.
@broteam168 The documentation page did not talk about any specific permissions, so I did not set any.
@redcurry, I have been in trouble with Android 10 permission API as well, from my research I found that this API was changed a little bit, to provide more privacy to the end-user. One of then is the Storage permission, It looks like you need to ask permission to access each folder that you need. To be honest I didn't look for more details on that.
For now, if you can I suggest you keep the target version to Android 9.0
@pictos Let me correct myself in something. I've always been targeting Android 9.0. What I changed was the emulated device. I got the exception on an Android 10.0 device, but not on an Android 9.0 device.
Thanks for the documentation link. I'll look into it.
Just a note, I had this problem when I forgot to add the Xamarin.Essentials NuGet package to the Android project. Then the necessary .xml file for the FileProvider
wasn't integrated into the Android app. Solved my problem.
I have updated documentation based on all of the changes in Android 10.
Sounds like we are good?
I have updated documentation based on all of the changes in Android 10.
James, not sure which page was updated but I can't find any "permissions" info on the Xamarin.Essentials: Share document page.
I've just tried the following code:-
string sDirPath = FileSystem.CacheDirectory;
string sFilePath = Path.Combine(sDirPath, sFilename);
File.WriteAllBytes(sFilePath, data);
// share the zip file
_ = Share.RequestAsync(new ShareFileRequest
{
File = new ShareFile(sFilePath),
Title = "Share Zipped Records"
});
... and I still get ...
[DatabaseUtils] java.lang.SecurityException: Permission Denial: reading xamarin.essentials.fileProvider uri content://xxxxxx.fileProvider/external_cache/2203693cc04e0be7f4f024d5f9499e13/c04154eb620d4dd3b9d4d5c404f4eb80/xxxxxxx.zip from pid=844, uid=1000 requires the provider be exported, or grantUriPermission()
[DatabaseUtils] at android.content.ContentProvider.enforceReadPermissionInner(ContentProvider.java:841)
[DatabaseUtils] at android.content.ContentProvider.semEnforceReadPermission(ContentProvider.java:758)
[DatabaseUtils] at android.content.ContentProvider$Transport.enforceReadPermission(ContentProvider.java:684)
[DatabaseUtils] at android.content.ContentProvider$Transport.query(ContentProvider.java:239)
[DatabaseUtils] at android.content.ContentProviderNative.onTransact(ContentProviderNative.java:106)
[DatabaseUtils] at android.os.Binder.execTransactInternal(Binder.java:1190)
[DatabaseUtils] at android.os.Binder.execTransact(Binder.java:1159)
@mpfj did you add the Xamarin.Essentials NuGet package to the Android project?
@jamesmontemagno yes ...
Hmmmm what we will want to see is if the file provider is being added into your file build https://github.com/xamarin/Essentials/blob/main/Xamarin.Essentials/Resources/xml/xamarin_essentials_fileprovider_file_paths.xml
Maybe try to add this manually to your Android app in Resources/xml/ with the same name and see if that fixes it.
And also if the file provider is being swizzled in https://github.com/xamarin/Essentials/blob/main/Xamarin.Essentials/Types/FileProvider.android.cs
Here it is not exported, but it is granting UI permission
What you want to do is going into your Android project folder on disk and go to obj/100 or obj/110, whatever you target.
Look inside of your AndroidManifest.xml in that folder and you will want to see:
<provider android:authorities="com.xamarin.essentials.fileProvider" android:exported="false" android:grantUriPermissions="true" android:name="xamarin.essentials.fileProvider">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/xamarin_essentials_fileprovider_file_paths" />
</provider>
Maybe try to add this manually to your Android app in Resources/xml/ with the same name and see if that fixes it.
(1) Added this but it made no difference.
Look inside of your AndroidManifest.xml in that folder and you will want to see:
<provider android:authorities="com.xamarin.essentials.fileProvider" android:exported="false" android:grantUriPermissions="true" android:name="xamarin.essentials.fileProvider"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/xamarin_essentials_fileprovider_file_paths" /> </provider>
(2) This is already present in the manifest file, so not that either :(
This also happened to me. Building with target Android version 11.0 (API 30) and running in a device with Android 11.
I figured out that happens when contentUris.Count == 1 in PlatformRequestAsync. Somehow Intent.ActionSendMultiple with intent.PutParcelableArrayListExtra works while Intent.ActionSend with intent.PutExtra(Intent.ExtraStream, contentUris[0]) doesn't.
I can create a PR to make it always handle it as SendMultiple but I'm not sure if it is the correct way to fix it.
Tested on Android 8, 9, 10, 11, 12 (Samsung and Huawei Devices)
AndroidManifest.xml
<provider android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true" android:name="androidx.core.content.FileProvider">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/sharing_paths" />
</provider>
sharing_paths.xml
<path>
...
<external-path name="Android/data/${applicationId}/ABC" path="."/>
..
</path>
Example.Java
import androidx.core.content.FileProvider;
...
try {
String mediaUri = "/storage/emulated/0/android/data/****.****.****.****/files/ABC/audiofile_202208180412_7596.m4a"; //for example
Intent intent = new Intent(Intent.ACTION_SEND);
File file = new File(mediaUri);
if (file.exists()) {
intent.setType("audio/*");
Uri uri = FileProvider.getUriForFile(this, APPLICATION_ID + ".provider", file);
intent.setClipData(ClipData.newRawUri("", uri));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.putExtra(Intent.EXTRA_STREAM, uri);
startActivity(Intent.createChooser(intent, "Sharing to..."));
}
}catch (IllegalArgumentException e) {
e.printStackTrace();
}
...
Can confirm that issue is still happening on android 11 with sample from the (https://learn.microsoft.com/en-us/xamarin/essentials/share?tabs=ios)