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

java.lang.SecurityException:Permission Denial:从ProcessRecord {430b1748 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; } 

按照请求清单的片段:

 <manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" package="com.xxx" > <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" /> 

过去几天有同样的问题。 尝试了一些解决scheme,但由于某种原因,我得到了一些图像的Uri内容提供商的权限拒绝,即使我已经将android.permission.MANAGE_DOCUMENTS权限添加到我的清单。

这是一个解决方法,暂时:

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

希望这可以帮助。

在用于提示用户添加文件的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)中所述,采用由Intent.ACTION_OPEN_DOCUMENT设置的以前的持久权限#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

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

  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(文件path),我能做到我想要什么打开后发生。

我现在试图弄清楚的问题是文件许可权持续了多久。 它似乎是从昨天过期,因为我的代码查找UriString,然后尝试打开文件。 如果我使用上面的代码来select图像,它的工作原理 – 它存储的名称,我可以退出程序,回去,它仍然工作…但是,我今天上午第一次运行程序(之后没有在12小时以上触摸它),它的行为就像无法find文件(我确信如果通过debugging器看,我会看到非关键性的错误再次出现。

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

我假设你想显示引用一个提供者的图像的Uri,这个提供者实现了Android 4.4 KitKat引入的新的存储访问框架。 这可以是图库以及谷歌驱动器。 访问这些文件的唯一方法似乎是使用系统文件选取器。 如果用户select要打开的文件,则系统将权限授予应用程序,从而启动文件打开对话框。 只要设备没有重新启动,这些权限就是有效的。 其他图像无法访问并导致安全exception。

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

MANAGE_DOCUMENTS权限只能由系统保存:

MANAGE_DOCUMENTS权限。 默认情况下,每个人都可以使用提供者。 添加此权限会限制您的提供者到系统。 这个限制对于安全是重要的。

(取自存储访问框架 )

因此,通过在您的DocumentProvider使用此权限,您只能将访问权限限制在系统中。 这意味着只有System UI picker才能够访问你的文件。 因此,为了使应用程序从您的Provider程序中select一个文件,必须使用ACTION_OPEN_DOCUMENT启动一个Intent ,然后启动System UI picker ,用户将从那里select一个文件,然后将URI返回您。

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

我不确定为什么其他方法不起作用。 但目前它是一个短期的修复。