I am trying to attach a file to an e-mail Intent but get a SecurityException in the Logcat and the e-mail client (gmail) reports "Unable to attach file":
java.lang.SecurityException: Permission Denial: reading com.pentad.bridge.FileProvider uri content://com.pentad.bridge.FileProvider/app_root/Android/data/com.pentad.bridge/files/Demo1.xml from pid=18762, uid=10199 requires the provider be exported, or grantUriPermission()
This only happens for some file suffixes it seems. Suffix ".brf" or ".txt" work fine, ".xml" reports the above error.
This happens on my Motorola G8 (Android 11) but a Motorola G6 (Android 9) works just fine. For the G8 I've had to add some notification code and suspect this is where the fault lies.
The error message indicates that I should export my FileProvider, but when I tried that in the Manifest I got:
Caused by: java.lang.SecurityException: Provider must not be exported
AndroidManifest.xml:
<provider
android:name="com.pentad.bridge.FileProvider"
android:authorities="com.pentad.bridge.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_provider_paths" />
</provider>
file_provider_paths:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="app_root" path="."/>
</paths>
Code FileProvider extends androidx.core.content.FileProvider:
// Build email Intent
Uri mailToUri = Uri.fromParts("mailto", "", null);
Intent emailIntent = new Intent(Intent.ACTION_SEND, mailToUri);
emailIntent.setType("text/plain");
emailIntent.putExtra(Intent.EXTRA_SUBJECT, aSubject);
emailIntent.putExtra(Intent.EXTRA_EMAIL, aStringArrayOfEmailAddress);
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
emailIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
emailIntent.putExtra(Intent.EXTRA_TEXT, aMessage);
// Build the attachment URI
File attachFile = new File(baseDirectory, aFileName);
Uri attachmentUri = androidx.core.content.FileProvider.getUriForFile(bridgeScorer, FileProvider.class.getCanonicalName(), attachFile);
emailIntent.putExtra(Intent.EXTRA_STREAM, attachmentUri);
// Try some other flags (which don't fix the problem).
// emailIntent.setClipData(ClipData.newRawUri("", attachmentUri));
// emailIntent.addFlags(Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
// emailIntent.addFlags(Intent.FLAG_GRANT_PREFIX_URI_PERMISSION);
// emailIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Start the email activity
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) // Earlier version can start e-mail Intent directly.
{
bridgeScorer.startActivity(emailIntent);
}
else
{
// Can't start e-mail directly, create a notification to do it.
// Create the NotificationChannel if on API 26+.
// Channel becomes permanently attached to the app (until the app is deleted).
// Change CHANNEL_ID if new parameters required.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
{
NotificationChannel channel = new NotificationChannel(NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME,
NotificationManager.IMPORTANCE_HIGH);
channel.setDescription("BridgeScorer E-mail request");
NotificationManager notificationManager = bridgeScorer.getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
PendingIntent pendingIntent = PendingIntent.getActivity(bridgeScorer, // Context
0, // A unique ID (within this app).
emailIntent, // Intent to start.
PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(bridgeScorer, NOTIFICATION_CHANNEL_ID)
.setCategory(Notification.CATEGORY_ALARM) // Try to over-ride DND.
.setSmallIcon(R.drawable.bridgescorer)
.setContentTitle(Config.getMessage("headingAppNotifyEmail"))
.setContentText(Config.getMessage("promptShareFile") + " " + aFileName)
.setLargeIcon(BitmapFactory.decodeResource(bridgeScorer.getResources(), R.drawable.bridgescorer))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setTimeoutAfter(NOTIFICATION_TIMEOUT)
.setAutoCancel(true)
.setFullScreenIntent(pendingIntent, false);
NotificationManagerCompat.from(bridgeScorer).notify(NOTIFICATION_ID, builder.build());
}
After one answer (thanks), I've swapped the addFlags and setFlags calls around and things are better, but I'm still getting the "requires the provider be exported, or grantUriPermission()" error but only in the following circumstances (as far as I can tell, it's quite confusing).
For each suffix (I use .html, .xml, .pbn, .dup and .brf), the first time I attach a file with the suffix, all is well. And I can repeat the process for the same suffix with the same file name any number of times.
But if I subsequently attach a file with a different name (but a suffix I've previously attached a file with) then I get this error.
I tried "force kill" my app but same behaviour.
If I re-boot the phone, then we're back to the start, I can attach any file with any suffix until I've used the suffix once. After that I have to use the same file name again for that suffix.
Your
setFlags()call replaces all existing flags, including the one you added the line above usingaddFlags(). You could invert the order of those lines to help clear up your problem.