Android - file provider - permission denial

I have two apps : app1 and app2.

App2 has :

<provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.android.provider.ImageSharing"
        android:exported="false"
        android:grantUriPermissions="true" >
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/paths" />
</provider>

paths.xml :

<paths>

     <files-path name="my_images" path="images/"/>

</paths>

App2 receives request in its Activity from App1 to get URI for an image. The App2 Activity does the following once URI is decided :

Intent intent = new Intent();

intent.setDataAndType(contentUri, getContentResolver().getType(contentUri));

int uid = Binder.getCallingUid();
String callingPackage = getPackageManager().getNameForUid(uid);

getApplicationContext().grantUriPermission(callingPackage, contentUri,
                    Intent.FLAG_GRANT_READ_URI_PERMISSION);

setResult(Activity.RESULT_OK, intent);
finish();

On receiving the result back from App2, App1 does the following :

Uri imageUri = data.getData();
if(imageUri != null) {
    ImageView iv = (ImageView) layoutView.findViewById(R.id.imageReceived);
    iv.setImageURI(imageUri);
}

In App1, on returning from App2, I get the following exception :

java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{52a99eb0 3493:com.android.App1.app/u0a57} (pid=3493, uid=10057) that is not exported from uid 10058

What am I doing wrong ?


Solution 1:

Turns out the only way to solve this is to grant permissions to all of the packages that might need it, like 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);
}

Solution 2:

Android <= Lollipop (API 22)

There's a great article by Lorenzo Quiroli that solves this issue for older Android versions.

He discovered that you need to manually set the ClipData of the Intent and set the permissions for it, like so:

if ( Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP ) {
    takePictureIntent.setClipData( ClipData.newRawUri( "", photoURI ) );
    takePictureIntent.addFlags( Intent.FLAG_GRANT_WRITE_URI_PERMISSION|Intent.FLAG_GRANT_READ_URI_PERMISSION );
}

I tested this on API 17 and it worked great. Couldn't find a solution anywhere that worked.

Solution 3:

First, I would try switching away from grantUriPermission() and simply put the FLAG_GRANT_READ_URI_PERMISSION on the Intent itself via addFlags() or setFlag().

If for some reason that does not work, you could try moving your getCallingUid() logic into onCreate() instead of wherever you have it, and see if you can find out the actual "caller" there.

Solution 4:

Just add setData(contentUri); and based on requirement add addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); or addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

This solves the java.lang.SecurityException: Permission Denial

Verified.

This is done as per https://developer.android.com/reference/android/support/v4/content/FileProvider.html#Permissions