Android KitKat securityException при попытке чтения из MediaStore
java.lang.SecurityException: отказ от разрешения: открытие провайдера com.android.providers.media.MediaDocumentsProvider из ProcessRecord {430b1748 29271: com.xxx/u0a88} (pid = 29271, uid = 10088) требует андроида .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
Была та же проблема за последние пару дней. Попробовал несколько решений, но по какой-то причине я получал отказ от разрешения на контент провайдера Uri для некоторых изображений, даже когда у меня появилось разрешение android.permission.MANAGE_DOCUMENTS в мой манифест.
В этом случае обходной путь:
i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, CHOOSE_IMAGE);
Это заставляет более старую галерею изображений открываться вместо нового представления документов Kitkat.
Теперь вы можете получить Uri, вызвав следующее в вашем onActivityResult:
Uri selectedImageURI = data.getData();
Надеюсь, что это поможет.
Ответ 2
В намерении, используемом для запроса пользователю добавить файл, используйте Intent.ACTION_OPEN_DOCUMENT (в KitKat API 19 или выше) вместо Intent.ACTION_GET_CONTENT или Intent.ACTION_PICK. Затем, когда вы хотите использовать Uri, возьмите предыдущие постоянные разрешения, которые были установлены Intent.ACTION_OPEN_DOCUMENT, как описано в ссылке @feri (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
Ответ 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 часов), он действовал так, как будто он не мог найти файл (и я уверен, что если смотреть через отладчик, я увижу, что некритическая ошибка появляется снова.
Итак, теперь возникает вопрос: как мы сохраняем права доступа, когда я знаю, что такое файл (имя)?
Ответ 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
, ему нужно будет запустить Intent
с ACTION_OPEN_DOCUMENT
, который фактически запустит System UI picker
, пользователь выберет файл оттуда, а затем URI
будет возвращен вам.
Ответ 6
Решение похоже использует http://developer.android.com/guide/topics/providers/document-provider.html#client вместо этого для доступа к изображениям из галереи
Я не уверен, почему другой подход не работает. Но на данный момент это надолго.