java.lang.SecurityException: Permission Denial: opening provider xxx.FileProvider

问题

Android 6.0 系统拍照后拿到Uri进行裁剪,结果报错如下:

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.android.gallery3d/com.android.gallery3d.filtershow.crop.CropActivity}: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{a7016cc 27355:com.android.gallery3d/u0a35} (pid=27355, uid=10035) that is not exported from uid 10074
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2583)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2665)
        at android.app.ActivityThread.-wrap11(ActivityThread.java)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1499)
        at android.os.Handler.dispatchMessage(Handler.java:111)
        at android.os.Looper.loop(Looper.java:207)
        at android.app.ActivityThread.main(ActivityThread.java:5765)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:789)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:679)
 Caused by: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord{a7016cc 27355:com.android.gallery3d/u0a35} (pid=27355, uid=10035) that is not exported from uid 10074
        at android.os.Parcel.readException(Parcel.java:1599)
        at android.os.Parcel.readException(Parcel.java:1552)
        at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:3735)
        at android.app.ActivityThread.acquireProvider(ActivityThread.java:5062)
        at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2023)
        at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1517)
        at android.content.ContentResolver.openTypedAssetFileDescriptor(ContentResolver.java:1121)
        at android.content.ContentResolver.openAssetFileDescriptor(ContentResolver.java:958)
        at android.content.ContentResolver.openInputStream(ContentResolver.java:678)
        at com.mediatek.gallery3d.util.DecodeSpecLimitor.isOutOfSpecLimit(DecodeSpecLimitor.java:39)
        at com.android.gallery3d.filtershow.crop.CropActivity.startLoadBitmap(CropActivity.java:240)
        at com.android.gallery3d.filtershow.crop.CropActivity.onCreate(CropActivity.java:158)
        at android.app.Activity.performCreate(Activity.java:6309)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1113)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2530)

原因

拿到的Uri为:

content://xxx/File/Pictures/JPEG_20180712_112510.jpg

可以看到格式为:content://authorities/定义的name属性/文件的相对路径,即name隐藏了可存储的文件夹路径。
7.0的手机运行是没有问题,但是7.0以下会Crash。
因为低版本的系统,仅仅是把这个当成一个普通的Provider在使用,而我们没有授权,contentprovider的export设置的也是false;导致Permission Denial。

解决的核心是:给需要访问的Uri授权。

给Uri授权后的裁剪代码如下

  /**
     * 裁剪
     * @param fragment
     * @param uri
     * @param requestCode
     */
    public static void startPhotoZoom(Fragment fragment, Uri uri, int requestCode) {
        Context context = fragment.getActivity();
        Intent intent = new Intent("com.android.camera.action.CROP");
        String filePath = FileUtil.getPath(context, "Image") + "/" + System.currentTimeMillis() + ".png";//裁剪后
        File file = new File(filePath);
        Uri crop_uri = getUriForFile(context, file);
        intent.setDataAndType(uri, "image/*");
        if (Build.VERSION.SDK_INT < 24) {
            grantPermissions(context, intent, uri, true);
        }
        //下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
        intent.putExtra("crop", "true");
        intent.putExtra("scale", true);
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", 600);
        intent.putExtra("outputY", 600);
        intent.putExtra("return-data", false);
        intent.putExtra("noFaceDetection", true); // no face detection
        intent.putExtra(MediaStore.EXTRA_OUTPUT, crop_uri);//裁剪后保存的路径Uri
        if (Build.VERSION.SDK_INT < 24) {
            grantPermissions(context, intent, crop_uri, true);
        }
        intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());//保存格式
        intent.putExtra("noFaceDetection", true); // no face detection
        fragment.startActivityForResult(intent, requestCode);
    }

    /**
     * 根据文件获取uri
     *
     * @param context
     * @param file
     * @return
     */
    public static Uri getUriForFile(Context context, File file) {
        Uri fileUri = null;
        if (Build.VERSION.SDK_INT >= 24) {
            fileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", file);
        } else {
            fileUri = Uri.fromFile(file);
        }
        return fileUri;
    }

    /**
     * @param context
     * @param intent
     * @param uri
     * @param writeAble 是否可读
     */
    public static void grantPermissions(Context context, Intent intent, Uri uri, boolean writeAble) {
        int flag = Intent.FLAG_GRANT_READ_URI_PERMISSION;
        if (writeAble) {
            flag |= Intent.FLAG_GRANT_WRITE_URI_PERMISSION;
        }
        intent.addFlags(flag);
        List<ResolveInfo> resInfoList = context.getPackageManager()
                .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            context.grantUriPermission(packageName, uri, flag);
        }
    }

参考

鸿神已经解释的很详细了,具体可以看这个:
https://blog.csdn.net/lmj623565791/article/details/72859156

猜你喜欢

转载自blog.csdn.net/demonliuhui/article/details/81013229