Подтвердить что ты не робот

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

Ответ 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 будет возвращен вам.