尝试从MediaStore读取时,Android KitKat securityException

时间:2013-11-07 13:34:54

标签: android android-4.4-kitkat

  

java.lang.SecurityException:Permission Denial:从ProcessRecord打开提供程序com.android.providers.media.MediaDocumentsProvider {430b1748 29271:com.xxx / u0a88}(pid = 29271,uid = 10088)需要android.permission.MANAGE_DOCUMENTS或者android.permission.MANAGE_DOCUMENTS

我添加了MANAGE_DOCUMENTSREAD_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" />

6 个答案:

答案 0 :(得分:45)

过去几天有同样的问题。尝试了一些解决方案,但出于某种原因,即使我已经将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();

希望这有帮助。

答案 1 :(得分:12)

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

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

答案 2 :(得分:11)

好的,我找到了一个可以解决问题的工作样本:

            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个多小时内触摸它,它表现得无法找到文件(而且我确定如果通过调试器观看,我会再次看到非严重错误。< / p>

现在的问题是:一旦我知道文件(名称)是什么,我们如何保留权限?

答案 3 :(得分:3)

我假设您想要显示Uri引用来自提供商的图像,该提供商实现了Android 4.4 KitKat引入的新存储访问框架。 这可以是Gallery和Google-Drive。 访问theese文档的唯一方法似乎是使用系统文件选择器。 如果用户选择要打开的文件,系统将授予应用程序权限,从而启动文件打开对话框。只要设备不是重新启动,Theese权限就是有效的。无法访问其他图像并导致安全例外。

如果要保留Uri,则还必须保留授予访问Uri的权限。有关详细信息,请查看此处 https://developer.android.com/guide/topics/providers/document-provider.html#permissions

答案 4 :(得分:3)

MANAGE_DOCUMENTS权限只能由系统保留:

  

MANAGE_DOCUMENTS权限。默认情况下,每个人都可以使用提供商。添加此权限会将您的提供程序限制在系统中。这种限制对安全很重要。

(摘自Storage Access Framework

因此,通过在DocumentProvider中使用此权限,您将限制对提供商的访问仅限于系统。这意味着只有System UI picker才能访问您的文件。因此,为了让应用从您的Provider中选择一个文件,它必须使用Intent启动ACTION_OPEN_DOCUMENT,这实际上会启动System UI picker,用户将从那里选择一个文件,然后URI将返回给你。

答案 5 :(得分:0)

解决方案似乎是使用http://developer.android.com/guide/topics/providers/document-provider.html#client来访问图库中的图像

我不确定为什么其他方法不起作用。但它现在是一个短期的解决方案。