Obtener excepción de denegación de permiso

Tengo una actividad en mi aplicación que le permite al usuario seleccionar varios archivos del dispositivo uno por uno, estoy usando una intención como esta:

Intent intent = new Intent(); intent.setType("*/*"); intent.setAction(Intent.ACTION_GET_CONTENT); startActivityForResult(Intent.createChooser(intent, getString(R.string.select_attachments_activity_chooser_label)), SELECT_PICTURE); 

Esto está funcionando perfectamente bien, estoy obteniendo el URI de los archivos seleccionados, se ven así:

 content://com.android.providers.media.documents/document/image%3A42555 

Entonces, si el archivo es una imagen, la estoy decodificando con:

 InputStream streamForDecodeBitmap = MyApp.getContext().getContentResolver().openInputStream(uri); Bitmap bitmap = BitmapFactory.decodeStream(streamForDecodeBitmap, null, options); 

Cuando el usuario hace clic en un botón, paso la lista de Uris a otra actividad por intención y en esta actividad, en AsyncTask, estoy codificando el archivo en base64 para enviarlo a través de la red:

 InputStream is = MyApp.getContext().getContentResolver().openInputStream(uri); byte[] inputData = getBytes(is); is.close(); return Base64.encodeToString(inputData, Base64.DEFAULT); 

El problema es cuando abro el inputStream, a veces funciona, pero la mayoría de las veces recibo esta excepción:

 E/AndroidRuntime(22270): Caused by: java.lang.SecurityException: Permission Denial: opening provider com.android.providers.media.MediaDocumentsProvider from ProcessRecord{42858fe0 22270:co.uk.manifesto.freeagentapp/u0a246} (pid=22270, uid=10246) requires android.permission.MANAGE_DOCUMENTS or android.permission.MANAGE_DOCUMENTS 

Estos son todos los permisos en mi manifiesto:

      

Estoy probando en un dispositivo con KITKAT (API 19).

Por favor, consulte estas preguntas también:
Android KitKat securityException al intentar leer desde MediaStore
Denegación de permiso: proveedor de apertura

Para el mismo problema en KitKat, utilicé esto. Es una opción / solución alternativa que encontré en uno de los enlaces de Stack Overflow, podrá seleccionar archivos desde Downloads / Recent.

 public static final int KITKAT_VALUE = 1002; Intent intent; if (Build.VERSION.SDK_INT < 19) { intent = new Intent(); intent.setAction(Intent.ACTION_GET_CONTENT); intent.setType("*/*"); startActivityForResult(intent, KITKAT_VALUE); } else { intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); intent.setType("*/*"); startActivityForResult(intent, KITKAT_VALUE); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == KITKAT_VALUE ) { if (resultCode == Activity.RESULT_OK) { // do something here } } } 

Referencia: Android Gallery en KitKat devuelve diferentes Uri para Intent.ACTION_GET_CONTENT http://developer.android.com/guide/topics/providers/document-provider.html#client

Cuando el control y los datos se devuelven a su actividad en

 protected void onActivityResult(int reqCode, int resultCode, Intent data) 

su actividad, al mismo tiempo, tiene permisos para acceder a la url de la imagen en el bash pasado. Su actividad podrá leer la url en el bash y mostrar la imagen. Pero no podrá pasar ningún permiso a otras actividades ni a un nuevo hilo de ejecución. Es por eso que obtiene SecurityException para android.permission.MANAGE_DOCUMENTS.

Una forma de evitar su problema de permiso es simplemente obtener los datos en la actividad que tiene permisos. En tu caso, de la actividad que tiene permiso haz tu encoding

 InputStream is = getContentResolver().openInputStream(uri); byte[] inputData = getBytes(is); is.close(); String encoded = Base64.encodeToString(inputData, Base64.DEFAULT); 

Una vez que tenga su cadena codificada, puede pasarla a cualquier otra actividad que pueda necesitarla, o puede pasarla a una AsyncTask para enviarla a otra parte.

Asegúrese de estar utilizando ContentResolver de la Actividad que solicitó el contenido, por ejemplo:

 activity.getContentResolver() 

En lugar de MyApp.getContext().getContentResolver()

Aparentemente fue el caso para mí y no necesitó agregar android.permission.MANAGE_DOCUMENTS al manifiesto.

Y que llamas el bash usando esto:

 if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { intent.setAction(Intent.ACTION_GET_CONTENT); } else { intent.setAction(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); } 

más información: https://developer.android.com/guide/topics/providers/document-provider.html#client

Escribir ACTION_OPEN_DOCUMENT no ayuda por completo. Después de reiniciar, su Permiso de URI ha desaparecido incluso con esta acción.

  if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { intent.setAction(Intent.ACTION_GET_CONTENT); } else { intent.setAction(Intent.ACTION_OPEN_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); } 

Para completar la respuesta de Alécio Carvalho mira esta documentación en la misma página. Hay una buena explicación con respecto a los Permisos URI y cómo hacer que persistan.

Simplemente agregue esto a su método onActivityResult y su permiso persiste a través de un reinicio del dispositivo. (Probado con un Google Pixel).

 override fun onActivityResult(requestCode:Int, resultCode:Int, intent:Intent?) { if(requestCode == SELECT_PICTURE && intent != null){ val uri = intent.getData() val takeFlags = intent.getFlags() and (Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION) activity.getContentResolver().takePersistableUriPermission(uri, takeFlags) PreferenceHelper.setHeaderImageUri(activity, uri.toString()) } } 

(Lo siento amante de kotlin ... la versión de java está dentro del enlace)

Vale la pena señalar que si el usuario elimina manualmente los permisos de un Uri que haya guardado previamente, obtendrá la misma excepción.

Configuración de Android -> Aplicación -> Nombre de la aplicación -> Almacenamiento -> Borrar acceso

Debes verificar que todavía tienes acceso al Uri cuando lo usas.

En kotlin resuelve colocando al operador "?" = (nullable) "?" = (nullable) después del bash

como el siguiente código:

 override func onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) }