69

我有两个应用程序: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>

路径.xml:

<paths>

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

</paths>

App2 在其 Activity 中从 App1 接收请求以获取图像的 URI。一旦确定了 URI,App2 Activity 就会执行以下操作:

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();

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

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

在 App1 中,从 App2 返回时,出现以下异常:

java.lang.SecurityException: Permission Denial: 从 ProcessRecord{52a99eb0 3493:com.android.App1.app/u0a57} (pid=3493, uid=10057) 打开提供程序 android.support.v4.content.FileProvider 不是从用户识别码 10058

我究竟做错了什么 ?

4

12 回答 12

68

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

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);
}
于 2015-11-17T10:45:29.680 回答
43

Android <= 棒棒糖 (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 );
}

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

于 2018-01-22T22:42:41.047 回答
39

首先,我会尝试切换grantUriPermission()并简单地通过or将其FLAG_GRANT_READ_URI_PERMISSION放在Intent自身上。addFlags()setFlag()

如果由于某种原因不起作用,您可以尝试将您的getCallingUid()逻辑移动到onCreate()您拥有它的任何地方,然后看看您是否可以在那里找到实际的“调用者”。

于 2014-06-28T14:41:19.183 回答
26

只需添加 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完成的

于 2017-01-11T12:02:06.553 回答
15

如何使用 File Provider 在 Nougat 上使用相机捕获图像。

要了解有关文件提供程序的信息,请点击此链接 文件提供程序

,kit 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>

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

<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_files" path="."/>

我已经解决了这个问题,它来自 kit 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;
}
于 2017-11-01T07:44:46.903 回答
3

我这样解决了这个问题:

        Intent sIntent = new Intent("com.appname.ACTION_RETURN_FILE").setData(uri);
        List<ResolveInfo> resInfoList = activity.getPackageManager().queryIntentActivities(sIntent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            activity.grantUriPermission(FILE_PROVIDER_ID, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
        sIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        activity.setResult(RESULT_OK, sIntent);
于 2017-04-11T12:13:49.710 回答
2

谢谢@CommonsWare 的建议。

我的问题是调用包。出于某种原因,Binder.callingUid()并且getPackageManager().getNameForUid(uid)给了我包名App2而不是App1.

我尝试在 App2onCreate和中调用它onResume,但没有任何乐趣。

我使用以下方法来解决它:

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

事实证明,activity 有专门的 API。见这里

于 2014-06-28T14:45:42.157 回答
2

您需要设置特定包名称的权限,然后您才能访问它..

context.grantUriPermission("com.android.App1.app", fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
于 2017-03-16T05:53:43.813 回答
1

确保每个应用程序的权限字符串都是唯一的。我克隆了另一个具有相同权限字符串的应用程序,导致错误

File file = new File("whatever");
URI uri = FileProvider.getUriForFile(context, "unique authority string", file);
于 2020-10-25T13:02:35.410 回答
0

如果有人在新版本的 Android 上遇到此问题,请确保您传递给 getUriForFile() 的权限字符串与您在清单文件中声明的相同。

于 2022-02-21T08:48:11.563 回答
0

就我而言,我要与文本共享图片,解决方案是添加两个标志,读取FLAG_GRANT_READ_URI_PERMISSION和写入FLAG_GRANT_WRITE_URI_PERMISSION如下:

        Intent intent = new Intent(android.content.Intent.ACTION_SEND);
    intent.putExtra(Intent.EXTRA_STREAM, uri);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    intent.setType("image/png");
    intent.putExtra(android.content.Intent.EXTRA_SUBJECT, getString(R.string.txtShareAppTitle));
    intent.putExtra(Intent.EXTRA_TEXT, getString(R.string.txtShareApp) ;
    startActivity(intent);
于 2020-06-20T02:32:08.110 回答
0

我是这样理解的

try{
    Intent(MediaStore.ACTION_IMAGE_CAPTURE).also { takePictureIntent ->
        takePictureIntent.resolveActivity(packageManager)?.also {
                val photoFile: File? = try {
                    createImageFile()
                } catch (ex: IOException) {
                    null
                }
            photoFile?.also {
                val photoURI: Uri = FileProvider.getUriForFile(
                    this,
                    "$packageName.fileprovider",
                    it
                )
                    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
                        val permissionTemp =  Intent.FLAG_GRANT_READ_URI_PERMISSION and Intent.FLAG_GRANT_WRITE_URI_PERMISSION
                        grantUriPermission(packageName, Uri.fromFile(photoFile), permissionTemp)
                        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile))
                    }else{
                        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI)
                    }
                startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
            }
        }
    }
}
    catch (ex: IOException){
    Log.d("IOException", "${ex.printStackTrace()}")
}

file_provider_paths.xml

<paths>
   <external-files-path name="my_images" path="/" />
   <external-path name="my_images" path="/"/>
   <external-path name="external" path="." />
   <external-files-path name="external_files" path="." />
   <files-path name="files" path="." />
</paths>

然后只需按照文档 拍照

于 2020-07-16T21:56:07.167 回答