Android KitKat SecurityException al intentar leer desde MediaStore


Java.lang.SecurityException: Denegación de permiso: apertura del proveedor com.androide.proveedor.medio.MediaDocumentsProvider from ProcessRecord{430b1748 29271: com.x.x. x/u0a88} (pid=29271, uid=10088) requiere android.permiso.MANAGE_DOCUMENTS o android.permiso.MANAGE_DOCUMENTS

He añadido los permisos MANAGE_DOCUMENTS y READ_EXTERNAL_STORAGE pero todavía estoy recibiendo este error. El código ofensivo:

 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;
}

Como fragmento solicitado del manifiesto:

<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" />
Author: Nitesh Verma, 2013-11-07

6 answers

Tuvo el mismo problema durante el último par de días. Probé algunas soluciones, pero por alguna razón estaba recibiendo denegación de permiso en el proveedor de contenido Uri para algunas imágenes, incluso cuando tenía el Android.permiso.Permiso MANAGE_DOCUMENTS añadido a mi manifiesto.

Aquí hay una solución, por el momento:

i = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(i, CHOOSE_IMAGE);

Esto obliga a abrir la antigua galería de imágenes en lugar de la nueva vista de documentos Kitkat.

Ahora, puede obtener el Uri llamando a lo siguiente en su onActivityResult:

Uri selectedImageURI = data.getData();

Espero que esto ayude.

 44
Author: rahulritesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-25 07:31:13

En la Intent utilizada para solicitar al usuario que agregue un archivo, use Intent.ACTION_OPEN_DOCUMENT (en KitKat API 19 o superior) en lugar de Intent.ACTION_GET_CONTENT o Intent.ACTION_PICK. Luego, cuando desee usar el Uri, tome los permisos persistentes anteriores que se establecieron por Intent.ACTION_OPEN_DOCUMENT como se describe en el enlace de @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);

Puedes leer más sobre las diferencias entre Intento.ACTION_GET_CONTENT, Intent.ACTION_PICK e Intent.ACTION_OPEN_DOCUMENT aquí: http://developer.android.com/reference/android/content/Intent.html#ACTION_OPEN_DOCUMENT

 11
Author: Tony Wickham,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-08 00:43:16

OK, encontré una muestra de trabajo que supera el problema:

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

Esto es lo que he aprendido...este es un cambio en KitKat, y este código solo funciona en API 19 y superiores. No funcionará para versiones anteriores, por lo que también debe tener métodos para pre-KitKat. Una vez que obtengas el archivo usando el argumento Category_Openable, en mi caso una imagen, puedes usarlo referenciándolo como el URI. En mi caso, almaceno el URIString (ruta del archivo) y puedo hacer lo que quiera con él después de la apertura ocurrir.

El problema que ahora estoy tratando de averiguar es cuánto tiempo persisten los permisos de archivo. Parece que expiró desde ayer, ya que mi código busca el UriString, luego intenta abrir el archivo. Si utilizo el código anterior para seleccionar la imagen, funciona-almacena el nombre, y pude salir del programa, volver y todavía funcionó...sin embargo, mi primera ejecución del programa esta mañana (después de no tocarlo en más de 12 horas), actuó como si no pudiera encontrar el archivo (y estoy seguro de si observo a través del depurador, vería aparecer de nuevo el error no crítico.

Así que ahora la pregunta es: ¿Cómo persistimos los permisos una vez que sé lo que es el archivo (nombre)?

 10
Author: user1082348,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-08-12 15:46:16

Asumo que el Uri que desea mostrar hace referencia a una imagen de un proveedor que implementa el nuevo Marco de Acceso al almacenamiento introducido con Android 4.4 KitKat. Esto puede ser Galería, así como Google-Drive. La única forma de acceder a estos documentos parece ser utilizar el selector de archivos del sistema. Si el usuario selecciona archivos para abrir, el sistema concede permiso a la aplicación, que inició el cuadro de diálogo abrir archivo. Estos permisos son válidos siempre y cuando el dispositivo no se reinicie. Otras imágenes no pueden ser accedido y conducir a la Excepción de Seguridad.

Si el Uri es persistido, entonces los derechos otorgados para acceder al Uri también deben ser persistidos. Para más detalles mira aquí: https://developer.android.com/guide/topics/providers/document-provider.html#permissions

 3
Author: feri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-02-08 00:29:41

El permiso MANAGE_DOCUMENTS solo puede ser retenido por el sistema:

El permiso MANAGE_DOCUMENTS. De forma predeterminada, un proveedor está disponible para todos. Agregar este permiso restringe el proveedor al sistema. Esta restricción es importante para la seguridad.

(tomado de Storage Access Framework)

Así que al usar este permiso en su DocumentProvider está restringiendo el acceso a su proveedor solo al sistema. Lo que esto significa es que solo el System UI picker será capaz de acceder a sus archivos. Por lo tanto, para que una aplicación elija un archivo de su Provider tendrá que iniciar un Intent con ACTION_OPEN_DOCUMENT, que de hecho iniciará el System UI picker, el usuario elegirá un archivo desde allí, y luego se le devolverá el URI.

 3
Author: Ovidiu Latcu,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-18 10:39:52

La solución parece estar usando http://developer.android.com/guide/topics/providers/document-provider.html#client en lugar de acceder a las imágenes de la galería

No estoy seguro de por qué el otro enfoque no funciona. Pero es una solución a corto plazo por ahora.

 0
Author: serenskye,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-11-13 11:18:20