Permission denied : opening provider com.google.android.apps.photos.contentprovider.MediaContentProvider that is not exported from uid
Asked Answered
B

5

21

My app is crashing only in android >= 6.0 (marshmallow) when I try to access a photo URI stored previously in SharedPreferences. Though the image is retrieved for the first time without any error. I am using targetSdkVersion 22 so that I don't need to handle run-time permissions in API >=23.

Permissions in my Manifest file

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.READ_SMS" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />

I am getting image as follows :

 // Determine Uri of camera image to save.
    final File root = new File(Environment.getExternalStorageDirectory() + File.separator + "pics" + File.separator);
    root.mkdirs();
    final String fname = Calendar.getInstance().getTimeInMillis() + ".jpg";
    final File sdImageMainDirectory = new File(root, fname);
    outputFileUri = Uri.fromFile(sdImageMainDirectory);

    // Camera.
    final List<Intent> cameraIntents = new ArrayList<Intent>();
    final Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
    final PackageManager packageManager = getPackageManager();
    final List<ResolveInfo> listCam = packageManager.queryIntentActivities(captureIntent, 0);
    for (ResolveInfo res : listCam) {
        final String packageName = res.activityInfo.packageName;
        final Intent intent = new Intent(captureIntent);
        intent.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
        intent.setPackage(packageName);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, outputFileUri);
        cameraIntents.add(intent);
    }

    // Filesystem.
    Intent galleryIntent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);

    // Chooser of filesystem options.
    final Intent chooserIntent = Intent.createChooser(galleryIntent, "Select Source");

    // Add the camera options.
    chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[cameraIntents.size()]));
    startActivityForResult(chooserIntent, Constants.IMAGE_PICKER);

This is my OnActivityResult method:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode == RESULT_OK) {
        if (requestCode == Constants.IMAGE_PICKER) {
            if (data == null) {
                selectedImageUri = outputFileUri;
            } else {
                selectedImageUri = data.getData();
            }
            if (selectedImageUri != null) {
                try {
                    String path = AppUtils.getPath(this, selectedImageUri);
                    if (path != null) {
                        Bitmap bitmap = AppUtils.decodeFile(new File(path));
                        File f = new File(System.currentTimeMillis() + ".jpg");
                        OutputStream os = new BufferedOutputStream(new FileOutputStream(f));
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
                        os.close();
                        uploadImageToServer(f, bitmap);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

where getPath() is function from https://mcmap.net/q/17801/-android-gallery-on-android-4-4-kitkat-returns-different-uri-for-intent-action_get_content.

stacktrace :

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.badiyajobs.app/com.badiyajobs.app.screens.ProfileActivity}: java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.MediaContentProvider from ProcessRecord{25e87b7 20432:com.badiyajobs.app/u0a389} (pid=20432, uid=10389) that is not exported from uid 10107
                                                                    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2426)
                                                                    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490)
                                                                    at android.app.ActivityThread.access$900(ActivityThread.java:154)
                                                                    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1354)
                                                                    at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                    at android.os.Looper.loop(Looper.java:148)
                                                                    at android.app.ActivityThread.main(ActivityThread.java:5443)
                                                                    at java.lang.reflect.Method.invoke(Native Method)
                                                                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728)
                                                                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
                                                                 Caused by: java.lang.SecurityException: Permission Denial: opening provider com.google.android.apps.photos.contentprovider.MediaContentProvider from ProcessRecord{25e87b7 20432:com.badiyajobs.app/u0a389} (pid=20432, uid=10389) that is not exported from uid 10107
                                                                    at android.os.Parcel.readException(Parcel.java:1620)
                                                                    at android.os.Parcel.readException(Parcel.java:1573)
                                                                    at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:3605)
                                                                    at android.app.ActivityThread.acquireProvider(ActivityThread.java:4799)
                                                                    at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2018)
                                                                    at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1466)
                                                                    at android.content.ContentResolver.query(ContentResolver.java:475)
                                                                    at android.content.ContentResolver.query(ContentResolver.java:434)
                                                                    at com.badiyajobs.app.utils.AppUtils.getDataColumn(AppUtils.java:790)
                                                                    at com.badiyajobs.app.utils.AppUtils.getPath(AppUtils.java:776)
                                                                    at com.badiyajobs.app.screens.ProfileActivity.setDefaultValue(ProfileActivity.java:192)
                                                                    at com.badiyajobs.app.screens.ProfileActivity.onCreate(ProfileActivity.java:162)
                                                                    at android.app.Activity.performCreate(Activity.java:6259)
                                                                    at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1130)
                                                                    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2379)
                                                                    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2490) 
                                                                    at android.app.ActivityThread.access$900(ActivityThread.java:154) 
                                                                    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1354) 
                                                                    at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                    at android.os.Looper.loop(Looper.java:148) 
                                                                    at android.app.ActivityThread.main(ActivityThread.java:5443) 
                                                                    at java.lang.reflect.Method.invoke(Native Method) 
                                                                    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:728) 
                                                                    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618) 

I don't know what am I missing. Please help.

Blague answered 5/11, 2016 at 13:1 Comment(5)
I try to access a photo URI stored previously in SharedPreferences.. Can be but you are not posting how and what you put in it. How you extract it and how you use it. So there is little to say.Maidinwaiting
You have a complicate pick intent. Never saw such. Intent.EXTRA_INITIAL_INTENTS, cameraIntents. what effect has this?Maidinwaiting
The SharedPreference part is irrelevent and working fine. This create a chooser for multiple apps.Blague
Dont do that while posting a problem here. Just take the simplest ACTION_PICK possible and then show how you save the picked uri to shared preferences and how you use it directly and afterwards. You did not post that code. This is the second time i ask you to do so.Maidinwaiting
I solved my problem using https://mcmap.net/q/245484/-how-to-persist-permission-in-android-api-19-kitkat . Thanks.Blague
I
26

Use ACTION_OPEN_DOCUMENT in your intent instead of ACTION_PICK. It allows long term, persistent access to files you request.

Intent galleryIntent = new Intent(Intent.ACTION_OPEN_DOCUMENT);

Documentation here: https://developer.android.com/guide/topics/providers/document-provider.html

Ichnography answered 13/4, 2017 at 18:3 Comment(0)
R
5

Create a class name RealPathUtil like below

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.CursorLoader;
import android.database.Cursor;
import android.net.Uri;
import android.provider.DocumentsContract;
import android.provider.MediaStore;

public class RealPathUtil {

@SuppressLint("NewApi")
public static String getRealPathFromURI_API19(Context context, Uri uri){
    String filePath = "";
    String wholeID = DocumentsContract.getDocumentId(uri);

     // Split at colon, use second item in the array
     String id = wholeID.split(":")[1];

     String[] column = { MediaStore.Images.Media.DATA };     

     // where id is equal to             
     String sel = MediaStore.Images.Media._ID + "=?";

     Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, 
                               column, sel, new String[]{ id }, null);

     int columnIndex = cursor.getColumnIndex(column[0]);

     if (cursor.moveToFirst()) {
         filePath = cursor.getString(columnIndex);
     }   

     cursor.close();

     return filePath;
}


@SuppressLint("NewApi")
public static String getRealPathFromURI_API11to18(Context context, Uri contentUri) {
      String[] proj = { MediaStore.Images.Media.DATA };
      String result = null;

      CursorLoader cursorLoader = new CursorLoader(
              context, 
        contentUri, proj, null, null, null);        
      Cursor cursor = cursorLoader.loadInBackground();

      if(cursor != null){
       int column_index = 
         cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
       cursor.moveToFirst();
       result = cursor.getString(column_index);
      }

      return result;  
}

public static String getRealPathFromURI_BelowAPI11(Context context, Uri contentUri){
           String[] proj = { MediaStore.Images.Media.DATA };
           Cursor cursor = context.getContentResolver().query(contentUri, proj, null, null, null);
           int column_index
      = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
           cursor.moveToFirst();
           return cursor.getString(column_index);
}
}

Then you can get the Real path in onActivityResult like

String realPath;
        // SDK < API11
        if (Build.VERSION.SDK_INT < 11)
            realPath = RealPathUtil.getRealPathFromURI_BelowAPI11(this, data.getData());

        // SDK >= 11 && SDK < 19
        else if (Build.VERSION.SDK_INT < 19)
            realPath = RealPathUtil.getRealPathFromURI_API11to18(this, data.getData());

        // SDK > 19 (Android 4.4)
        else
            realPath = RealPathUtil.getRealPathFromURI_API19(this, data.getData());

Resalt:

content://com.android.providers.media.documents/document/image:36

to

/storage/emulated/0/Pictures/JPEG_20190812_163204_299063245992150918.jpg

Hope this was helpful!!

Repartee answered 12/8, 2019 at 12:26 Comment(1)
Is it possible to simply use the "SDK < 11" version on all SDK versions? Or do we have to have different methods for each bracket?Kaila
B
3

Improving the @Ashton Coulson's answer, you can handle the Kitkat version to use this parameter:

String action;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
    action = Intent.ACTION_OPEN_DOCUMENT;
} else {
    action = Intent.ACTION_PICK;
}
Intent intent = new Intent(action, uri);

The if is needed because as stated in the documentation, the Storage Access Framework (SAF) was introduced in Android 4.4 (API level 19).

Brander answered 16/10, 2017 at 16:56 Comment(0)
M
1

You should take a persistable uri permission:

        Uri uri = intent.getData();

        context.getContentResolver().takePersistableUriPermission(uri
                   , intent.getFlags() 
                          & ( Intent.FLAG_GRANT_READ_URI_PERMISSION 
                          + Intent.FLAG_GRANT_WRITE_URI_PERMISSION 
                          )
                        ); 
Maidinwaiting answered 5/11, 2016 at 13:45 Comment(1)
now I am getting No persistable permission grants found for UID 10389 and Uri 0 @ content://com.google.android.apps.photos.contentprovider/0/1/content://media/external/images/media/41287/ORIGINAL/NONE/1381709033Blague
I
0

you can add this line:

intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION); intent.addFlags(FLAG_GRANT_PERSISTABLE_URI_PERMISSION);

Inesita answered 2/2, 2018 at 14:39 Comment(1)
Doesn't work - same error.Gorden

© 2022 - 2024 — McMap. All rights reserved.