xamarin / Essentials

Xamarin.Essentials is no longer supported. Migrate your apps to .NET MAUI, which includes Maui.Essentials.
https://aka.ms/xamarin-upgrade
Other
1.53k stars 504 forks source link

[Bug] Share.RequestAsync causes java.lang.SecurityException: Permission Denial #1314

Open redcurry opened 4 years ago

redcurry commented 4 years ago

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

  1. Create a file in Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData).
  2. 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

pictos commented 4 years ago

@redcurry I'm guessing that is caused by the change on Android 10 permissions. If you target Android9.0 this error occurs?

broteam168 commented 4 years ago

Are you missing a perrmision granted? String packageName = resolveInfo.activityInfo.packageName; context.grantUriPermission(some permissions);

redcurry commented 4 years ago

@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.

pictos commented 4 years ago

@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

redcurry commented 4 years ago

@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.

vividos commented 3 years ago

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.

jamesmontemagno commented 3 years ago

I have updated documentation based on all of the changes in Android 10.

Sounds like we are good?

mpfj commented 3 years ago

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)
jamesmontemagno commented 3 years ago

@mpfj did you add the Xamarin.Essentials NuGet package to the Android project?

mpfj commented 3 years ago

@jamesmontemagno yes ...

image

jamesmontemagno commented 3 years ago

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>
mpfj commented 3 years ago

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 :(

lucasbrahm commented 2 years ago

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.

FaakhirIqbal commented 2 years ago

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();
}
 ...        
WanftMoon commented 1 year ago

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)