尝试从 MediaStore 读取时出现 Android KitKat securityException

Posted

技术标签:

【中文标题】尝试从 MediaStore 读取时出现 Android KitKat securityException【英文标题】:Android KitKat securityException when trying to read from MediaStore 【发布时间】:2013-11-19 04:22:14 【问题描述】:

java.lang.SecurityException: Permission Denial: 从 ProcessRecord430b1748 29271:com.xxx/u0a88 (pid=29271, uid=10088) 打开提供程序 com.android.providers.media.MediaDocumentsProvider 需要 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;

根据要求的清单sn-p:

<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" />

【问题讨论】:

您是否正确添加了权限?也许发布manafest? 你需要发布整个manafest 我的观点相同。小米应用适用于 【参考方案1】:

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

希望这会有所帮助。

【讨论】:

很好的解决方法,但这只能解决一个小问题,但我使用其他东西来阅读视频,我该如何解决我的问题?【参考方案2】:

在用于提示用户添加文件的 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

【讨论】:

【参考方案3】:

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

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

这是我学到的……这是 KitKat 中的一个更改,此代码仅适用于 API 19 及更高版本。它不适用于旧版本,因此您还需要有用于 pre-KitKat 的方法。使用 Category_Openable 参数获取文件后,在我的例子中是图像,然后您可以通过将其引用为 URI 来使用它。就我而言,我存储了 URIString(文件路径),并且在打开发生后我可以用它做我想做的事情。

我现在要弄清楚的问题是文件权限持续多长时间。它似乎从昨天开始过期,因为我的代码查找 UriString,然后尝试打开文件。如果我使用上面的代码选择图像,它可以工作 - 它存储名称,并且我能够退出程序,返回并且它仍然有效......但是,我今天早上第一次运行程序(之后在 12 多个小时内没有触摸它),它表现得好像找不到文件(我敢肯定,如果通过调试器观看,我会再次看到非严重错误。

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

【讨论】:

19 岁及以上的工作解决方案【参考方案4】:

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

如果 Uri 被持久化,那么授予访问 Uri 的权限也必须被持久化。更多详情请看这里: https://developer.android.com/guide/topics/providers/document-provider.html#permissions

【讨论】:

【参考方案5】:

MANAGE_DOCUMENTS 权限只能由系统持有:

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

(取自Storage Access Framework)

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

【讨论】:

【参考方案6】:

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

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

【讨论】:

以上是关于尝试从 MediaStore 读取时出现 Android KitKat securityException的主要内容,如果未能解决你的问题,请参考以下文章

尝试从 Java 中的二进制文件中读取 Int 时出现 BufferUnderflowException

尝试从主机连接到 mysql docker 容器时出现“读取初始通信数据包”错误

尝试从 BigQuery 读取表并使用 Airflow 将其保存为数据框时出现 _pickle.PicklingError

尝试从 WinJS 读取 C# WinRT 组件中的空字符串时出现异常

Angular 10 + PubNub - 从特定频道读取消息时出现问题

尝试使用帐户服务从 Gmail 帐户读取 Gsuite 中的公司电子邮件时出现 401 未经授权的错误