Permission Denial with File Provider through intent
H

4

14

I am attempting send a bitmap from the cache directory of my app to the text messaging app. I am using file provider to grant temporary permission to the application that handles the intent. When I try to send the intent and I select the default android text messaging app from the intent chooser my message app crashes and I get this error. I tried selecting other applications from the intent chooser such as email and other messaging apps and it seemed to work fine, only crashes with the default text messaging app.

java.lang.SecurityException: Permission Denial: reading android.support.v4.content.FileProvider uri content://com.example.brandon.emojimms2/shared_images/image.png from pid=9804, uid=10024 requires the provider be exported, or grantUriPermission()

Permission Denial Here is the code where I share the intent

private void shareImage()
{
    File imagePath = new File(mContext.getCacheDir(), "images");
    File newFile = new File(imagePath, "image.png");
    Uri contentUri = FileProvider.getUriForFile(mContext, "com.example.brandon.emojimms2", newFile);

    if (contentUri != null) {
        Intent shareIntent = new Intent();
        shareIntent.setAction(Intent.ACTION_SEND);
        shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // temp permission for receiving app to read this file
        shareIntent.setDataAndType(contentUri, mContext.getContentResolver().getType(contentUri));
        shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
        startActivity(Intent.createChooser(shareIntent, "Choose an app"));
    }
}

I am fairly certain I set up the File provider correctly, but here is the manifest in case it is needed. enter image description here

Edit: I just did some testing and it seems that the crash with the text messaging app is happening on phones on earlier apis, but is working on new apis such as 7.1. Did either the text messaging app or with the way you were supposed to grant uri read permissions change?

Hebetude answered 26/8, 2017 at 8:1 Comment(2)
Please post in a normal way: text. No images please.Madder
I've been doing some testing and it seems the error may be coming from the fact that using the file provider with the older messaging system com.android.mms causes this permission error, because the versions of android that run the newer messaging system com.android.messaging seem to work fine.Hebetude
S
16

Try this:

List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    context.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
Sicular answered 26/8, 2017 at 8:28 Comment(1)
I tried this and it works like a charm. Also there's similar question where the answer is based on this one: https://mcmap.net/q/830162/-permission-denial-while-sharing-file-with-fileprovider-duplicatePvc
T
2

Tested on Android 8, 9, 10, 11, 12 (Samsung and Huawei Devices) Audio file

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();
}
 ...        

It can be Image (.JPG,.PNG) video files too, which can be changed accordingly.

intent.setType("audio/*"); 
Tirzah answered 18/8, 2022 at 11:14 Comment(0)
A
0

For this example

Intent intentShareFile = new Intent(Intent.ACTION_SEND);
File fileWithinMyDir = new File(targetPdf);

if (fileWithinMyDir.exists()) {
        intentShareFile.setType("application/pdf");
        Uri uri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".provider", fileWithinMyDir);
        intentShareFile.setClipData(ClipData.newRawUri("", uri));
        intentShareFile.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
        startActivity(Intent.createChooser(intentShareFile, "sending file..."));
}

For images:

Change

intentShareFile.setType("application/pdf");

to

intentShareFile.setType("image/*");

It's work in Android 10, 11, 12

https://developer.android.com/reference/androidx/core/content/FileProvider

Artery answered 14/8, 2022 at 12:45 Comment(0)
O
0

To add to the many existing answers, and hopefully solve one issue I saw on Android 11 and up for others:

Many methods only use intent.putExtra(Intent.EXTRA_STREAM, uri). When sharing images, I had to use intent.setDataAndType(uri, getContentResolver().getType(uri)) for the newer send dialog to have access to the image and not throw the permission denied error. Without setDataAndType(), the app selected from the picker could still receive the image, but it would throw a nuisance exception and the share dialog could not preview the image.

My full code, edited a bit for readability:

File file = new File(path);
if (!file.exists()) return;
try {
    Uri uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", file);
    Intent sharingIntent = new Intent(Intent.ACTION_SEND);
    sharingIntent.setDataAndType(uri, getContentResolver().getType(uri));
    sharingIntent.putExtra(Intent.EXTRA_STREAM, uri);
    sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    Intent chooser = Intent.createChooser(sharingIntent, "Share Image");
    startActivity(chooser);
} catch (Exception e) {
    Log.e("SharingSnippet", e.toString());
}

From my research:

The current top voted answer involves calling context.grantUriPermission() on all of the resolved activities. This is not recommended, as you then should revoke that permission later, else it remains a security hole.

Call the method Context.grantUriPermission(package, Uri, mode_flags) for the content:// Uri, using the desired mode flags. This grants temporary access permission for the content URI to the specified package, according to the value of the the mode_flags parameter, which you can set to FLAG_GRANT_READ_URI_PERMISSION, FLAG_GRANT_WRITE_URI_PERMISSION or both. The permission remains in effect until you revoke it by calling revokeUriPermission() or until the device reboots.

https://developer.android.com/reference/android/support/v4/content/FileProvider#Permissions

ClipData is only supported in Android 4.1 and up. Any users you would support should be there, but it's something to consider. Not all receiving apps are written well enough to handle multiple files, either.

Note: The Intent.setClipData() method is only available in platform version 16 (Android 4.1) and later. If you want to maintain compatibility with previous versions, you should send one content URI at a time in the Intent. Set the action to ACTION_SEND and put the URI in data by calling setData().

(same page as above)

In the paths.xml file, <external-path name="root" path="." /> is what you need for the root of the internal storage on most devices. As far as I could find, there is no official support for external storage (SD cards, USB, etc) via the FileProvider. Your options are to make your own, find one on GitHub, or use the undocumented and deprecated <root-path name="root" /> tag to give access to everything that the app can access.

Ovi answered 20/2, 2023 at 22:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.