Android - 文件提供程序 - 权限拒绝

Jak*_*ake 58 android android-fileprovider

我有两个应用程序:app1和app2.

App2有:

<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>
Run Code Online (Sandbox Code Playgroud)

paths.xml:

<paths>

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

</paths>
Run Code Online (Sandbox Code Playgroud)

App2从App1的Activity中接收请求以获取图像的URI.确定URI后,App2活动执行以下操作:

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();
Run Code Online (Sandbox Code Playgroud)

从App2收到结果后,App1执行以下操作:

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

在App1中,从App2返回时,我得到以下异常:

java.lang.SecurityException:Permission Denial:从ProcessRecord {52a99eb0 3493:com.android.App1.app/u0a57}(pid = 3493,uid = 10057)打开提供者android.support.v4.content.FileProvider uid 10058

我究竟做错了什么 ?

lim*_*lim 64

事实证明,解决此问题的唯一方法是为可能需要它的所有软件包授予权限,如下所示:

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);
}
Run Code Online (Sandbox Code Playgroud)

  • 对于前Lollipop版本,这可能是正确的解决方案.见[答案]的评论(http://stackoverflow.com/a/33652695/1181162) (3认同)
  • 事实上,这个 hack 是唯一真正有效的解决方案(在浪费了很多时间之后),这对于 Android 生态系统来说确实是可耻的! (2认同)

Jos*_*ter 35

Android <= Lollipop(API 22)

Lorenzo Quiroli 有一篇很棒的文章解决了旧版Android版本的问题.

他发现你需要手动设置Intent的ClipData并为其设置权限,如下所示:

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 );
}
Run Code Online (Sandbox Code Playgroud)

我在API 17上对此进行了测试,效果很好.无法在任何有效的地方找到解决方案.


Com*_*are 34

首先,我会尝试从切换掉grantUriPermission(),只是把FLAG_GRANT_READ_URI_PERMISSIONIntent本身通过addFlags()setFlag().

如果由于某些原因不起作用,您可以尝试将getCallingUid()逻辑移动到onCreate()您所拥有的任何地方,并查看是否可以找到实际的"调用者".

  • 不适用于 Android 8.0。唯一有帮助的是将所有 resolveInfos 和 grantUriPermission() 循环给所有这些。多么野蛮的做法。为你感到羞耻,谷歌 (2认同)

小智 22

只需添加 setData(contentUri); 并根据需求添加 addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);

这解决了java.lang.SecurityException:Permission Denial

验证.

这是根据 https://developer.android.com/reference/android/support/v4/content/FileProvider.html#Permissions完成的


Naj*_*Ali 13

如何使用文件提供程序在Nougat上使用相机捕获图像.

,套件kat和marshmallow遵循这些步骤.首先是MainfestFile中应用程序标记下的广告代码提供程序.

 <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths"/>
    </provider>
Run Code Online (Sandbox Code Playgroud)

在res文件夹下使用(provider_paths.xml)创建文件名 在此输入图像描述

<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>
Run Code Online (Sandbox Code Playgroud)

我已经解决了这个问题,它来自kitkat版本

private void takePicture() {
    Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
        Uri photoURI = null;
        try {
            File photoFile = createImageFileWith();
            path = photoFile.getAbsolutePath();
            photoURI = FileProvider.getUriForFile(MainActivity.this,
                    getString(R.string.file_provider_authority),
                    photoFile);

        } catch (IOException ex) {
            Log.e("TakePicture", ex.getMessage());
        }
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
        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);
        }
        startActivityForResult(takePictureIntent, PHOTO_REQUEST_CODE);
    }
}

  private File createImageFile() throws IOException {
    // Create an image file name
    String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.ENGLISH).format(new Date());
    String imageFileName = "JPEG_" + timeStamp + "_";
    File storageDir = new File(Environment.getExternalStoragePublicDirectory(
            Environment.DIRECTORY_DCIM), "Camera");
    File image = File.createTempFile(
            imageFileName,  /* prefix */
            ".jpg",         /* suffix */
            storageDir      /* directory */
    );

    // Save a file: path for use with ACTION_VIEW intents
    mCurrentPhotoPath = "file:" + image.getAbsolutePath();
    return image;
}
Run Code Online (Sandbox Code Playgroud)