Permission Denial while sharing file with FileProvider

In addition to the answer of Nit above, it is possible to set the ClipData directly with the to be shared uri as follows.

intent.setClipData(ClipData.newRawUri("", uri));

This prevents the security exception when presenting the intent chooser.

If you want to share multiple uris, you can set the clipdata with multiple uris to prevent the security exception. To summarize:


Intent intent = new Intent();
intent.setType(mimeType);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

if (uris.size() == 0) { 
   return;
}
else if (uris.size() == 1) {
   Uri uri = uris.get(0);
   intent.setAction(Intent.ACTION_SEND);
   intent.putExtra(Intent.EXTRA_STREAM, uri);
   intent.setClipData(ClipData.newRawUri("", uri));   
}
else {
  intent.setAction(Intent.ACTION_SEND_MULTIPLE);
  intent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);
  ClipData clipData = ClipData.newRawUri("", uris.get(0));

  for (int i = 1; i < uris.size(); i++) { 
      Uri uri = uris.get(i); 
      clipData.addItem(new ClipData.Item(uri));
  }
  intent.setClipData(clipData);

}

startActivity(Intent.createChooser(intent, title));


There is also a way to grant the URI permission through the Intent flag, without doing in manually with grantUriPermission() and by keeping the usage of Intent.createChooser().

The Intent.createChooser() documentation states:

If the target intent has specified FLAG_GRANT_READ_URI_PERMISSION or FLAG_GRANT_WRITE_URI_PERMISSION, then these flags will also be set in the returned chooser intent, with its ClipData set appropriately: either a direct reflection of getClipData() if that is non-null, or a new ClipData built from getData().

Therefore, if the original Intent has the Uri set in its ClipData or in setData(), it should work as wanted.

In your example, the ACTION_SEND Intent supports the Uri set through setClipData() as of Jelly Bean (Android 4.1).

Long story short, here is a working example of your code:

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

if(fileWithinMyDir.exists()) {
    String mimeType = "application/pdf";
    String[] mimeTypeArray = new String[] { mimeType };
    
    intentShareFile.setType(mimeType);
    Uri uri = FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID + ".provider", fileWithinMyDir);
    
    // Add the uri as a ClipData
    intentShareFile.setClipData(new ClipData(
        "A label describing your file to the user",
        mimeTypeArray,
        new ClipData.Item(uri)
    ));
    
    // EXTRA_STREAM is kept for compatibility with old applications
    intentShareFile.putExtra(Intent.EXTRA_STREAM, uri);
    
    intentShareFile.putExtra(Intent.EXTRA_SUBJECT,
            "Sharing File...");
    intentShareFile.putExtra(Intent.EXTRA_TEXT, "Sharing File...");
    intentShareFile.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivity(Intent.createChooser(intentShareFile, "Share File"));
}

Sorry about the late response. I solved it like this:

Intent chooser = Intent.createChooser(intentShareFile, "Share File");

List<ResolveInfo> resInfoList = this.getPackageManager().queryIntentActivities(chooser, PackageManager.MATCH_DEFAULT_ONLY);

for (ResolveInfo resolveInfo : resInfoList) {
    String packageName = resolveInfo.activityInfo.packageName;
    this.grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
}

startActivity(chooser);

This helped me: Permission Denial with File Provider through intent