Android - 文件提供者 - 权限拒绝

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/24467696/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 08:14:40  来源:igfitidea点击:

Android - file provider - permission denial

androidandroid-fileprovider

提问by Jake

I have two apps : app1 and app2.

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

App2 has :

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>

paths.xml :

路径.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 :

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

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

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

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 :

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

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

java.lang.SecurityException: Permission Denial: opens provider android.support.v4.content.FileProvider from ProcessRecord{52a99eb0 3493:com.android.App1.app/u0a57} (pid=3493, uid=10057) 不是从用户名 10058

What am I doing wrong ?

我究竟做错了什么 ?

回答by limlim

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

回答by Joshua Pinter

Android <= Lollipop (API 22)

Android <= Lollipop (API 22)

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

Lorenzo Quiroli的一篇很棒的文章解决了旧 Android 版本的这个问题。

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

他发现需要手动设置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 );
}

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

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

回答by CommonsWare

First, I would try switching away from grantUriPermission()and simply put the FLAG_GRANT_READ_URI_PERMISSIONon the Intentitself via addFlags()or setFlag().

首先,我会尝试从切换掉grantUriPermission(),只是把FLAG_GRANT_READ_URI_PERMISSIONIntent本身通过addFlags()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.

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

回答by RamiReddy

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

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

This solves the java.lang.SecurityException: Permission Denial

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

Verified.

验证。

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

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

回答by Najaf Ali

How you can capture image using camera on Nougat using File Provider.

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

To read about about file provider follow this link File Provider

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

,and kit kat and marshmallow follow these steps. First of all ad tag provider under application tag in MainfestFile.

,kit kat 和棉花糖按照以下步骤操作。首先是 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>

create a file name with (provider_paths.xml) under res folder enter image description here

在 res 文件夹下创建一个带有 (provider_paths.xml) 的文件名 在此处输入图片说明

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

I have solved this issue it comes on kit kitkat version

我已经解决了这个问题,它来自 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;
}

回答by Rainmaker

I solved the problem that way:

我是这样解决问题的:

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

回答by Jd Prajapati

You need to set permission of specific package name, after that you can able to access it..

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

context.grantUriPermission("com.android.App1.app", fileUri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);

回答by Jake

Thanks, @CommonsWare for this advice.

谢谢,@CommonsWare 的建议。

My problem was with the calling package. For some reason, Binder.callingUid()and getPackageManager().getNameForUid(uid)was giving me package name of App2instead of App1.

我的问题是调用包。出于某种原因,Binder.callingUid()getPackageManager().getNameForUid(uid)准备给我包的名称App2来代替App1

I tried calling it in App2's onCreateas well as onResume, but no joy.

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

I used the following to solve it :

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

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

Turns out, activity has dedicated API for this. See here.

事实证明,活动为此具有专用的 API。见这里