尝试从MediaStore读取时出现Android KitKat安全性异常

46
java.lang.SecurityException: 权限拒绝:从ProcessRecord{430b1748 29271:com.x.x.x/u0a88} (pid=29271, uid=10088) 打开提供者com.android.providers.media.MediaDocumentsProvider需要android.permission.MANAGE_DOCUMENTS或android.permission.MANAGE_DOCUMENTS。
我已添加了MANAGE_DOCUMENTS和READ_EXTERNAL_STORAGE权限,但仍然出现此错误。有问题的代码为:
 public static String getImagePath(HailoDriverApplication app, Uri uri) {
    Cursor cursor = null;
    if (uri == null) {
        return null;
    }
    try {
        cursor = app.getContentResolver().query(uri, new String[] {
            MediaStore.Images.Media.DATA
        }, null, null, null);
        int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
        if (cursor.moveToFirst()) {
            return cursor.getString(column_index);
        }
    } finally {
        if (cursor != null) {
            cursor.close();
        }
    }
    return null;
}

如所请求,清单摘录:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.x.x.x" >

<uses-sdk
    android:minSdkVersion="8"
    android:targetSdkVersion="16" />

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COURSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.READ_LOGS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
<uses-permission
    android:name="android.permission.UPDATE_DEVICE_STATS"
    tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.intent.action.BATTERY_CHANGED" />
<uses-permission
    android:name="android.permission.INSTALL_PACKAGES"
    tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />

1
你是否正确添加了权限?或许可以发布清单(manifest)文件? - Broak
1
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS" /> - serenskye
1
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> - serenskye
1
你需要发布整个清单。 - Broak
2
我遇到了相同的问题。我的应用程序可以在低于4.4版本的安卓系统上运行,但无法在Nexus 5(4.4)上运行。 - Jose Manuel
显示剩余8条评论
6个回答

44

最近几天我遇到了同样的问题。尝试了几种解决方案,但是不知什么原因,即使我在清单文件中添加了android.permission.MANAGE_DOCUMENTS权限,仍然会在Uri内容提供程序上获得权限拒绝错误。

暂时的解决办法如下:

i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, CHOOSE_IMAGE);

这将强制使用旧的图像库而非新的Kitkat文档视图。

现在,您可以通过在onActivityResult中调用以下内容来获取Uri:

Uri selectedImageURI = data.getData();

希望这能帮到您。


很好的解决方法,但那只修复了一个小问题,我使用其他工具来读取视频,我该如何解决我的问题? - Samuel Thompson

13

在用于提示用户添加文件的Intent中,使用Intent.ACTION_OPEN_DOCUMENT(适用于KitKat API 19或更高版本),而不是Intent.ACTION_GET_CONTENT或Intent.ACTION_PICK。然后,在您想要使用Uri时,请按照@feri链接中描述的方式获取先前由Intent.ACTION_OPEN_DOCUMENT设置的持久权限(https://developer.android.com/guide/topics/providers/document-provider.html#permissions):

final int takeFlags = data.getFlags()
        & (Intent.FLAG_GRANT_READ_URI_PERMISSION
        | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
// Check for the freshest data.
getContentResolver().takePersistableUriPermission(originalUri, takeFlags);

您可以在此处阅读有关 Intent.ACTION_GET_CONTENT、Intent.ACTION_PICK 和 Intent.ACTION_OPEN_DOCUMENT 之间区别的更多信息:http://developer.android.com/reference/android/content/Intent.html#ACTION_OPEN_DOCUMENT


12

好的,我找到了一个有效的示例来解决这个问题:

            intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
            intent.addCategory(Intent.CATEGORY_OPENABLE);
            intent.setType("image/*");
            startActivityForResult(intent, myClassConstant.SELECT_PICTURE);

以下是我学到的内容...这是KitKat中的更改,这段代码仅适用于API 19及以上版本。它不适用于旧版本,因此您需要为KitKat之前的方法进行处理。使用Category_Openable参数获取文件后,可以像引用URI一样使用它,比如在我这个案例里,是一个图像。我将URIString(文件路径)存储下来,打开后就能按照我的想法使用它。

现在要解决的问题是文件权限持续多久。似乎它已经过期了,因为昨天我的代码查找UriString,然后尝试打开文件。如果我使用上面的代码选择图像,它可以正常工作——存储了名称,我能够退出程序,重新进入仍然可以工作...但今天早上第一次运行程序时(12小时以上未接触),它表现得好像找不到文件(如果通过调试器观察,我肯定会看到非关键性错误再次出现)。

所以现在的问题是:我们如何在知道文件(名称)后持久保存权限?


19及以上的工作解决方案 - FireZenk

3
我假设您想要显示的Uri引用了一个实现了Android 4.4 KitKat引入的新存储访问框架的提供程序中的图像。这可以是Gallery,也可以是Google Drive。 似乎唯一访问这些文档的方法是使用系统文件选择器。 如果用户选择打开文件,则系统会授予启动文件打开对话框的应用程序权限。只要设备没有重新启动,这些权限就有效。其他图像无法访问,会导致安全异常。 如果要持久保存Uri,则必须持久保存访问Uri所授予的权限。有关更多详细信息,请参见此处: https://developer.android.com/guide/topics/providers/document-provider.html#permissions

3
MANAGE_DOCUMENTS 权限只能被系统持有:

MANAGE_DOCUMENTS 权限。默认情况下,提供程序对所有人都可用。添加此权限会将您的提供程序限制为系统。这种限制对于安全性很重要。

(取自存储访问框架
因此,在您的 DocumentProvider 中使用此权限会将对您的提供程序的访问限制仅限于系统。这意味着只有“系统 UI 选择器”才能够访问您的文件。因此,为了从您的 Provider 中选择一个文件,应用程序需要启动一个带有 ACTION_OPEN_DOCUMENTIntent,这实际上将启动“系统 UI 选择器”,用户将从中选择一个文件,然后将返回 URI 给您。

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接