Solicitar y permitir permiso WRITE_EXTERNAL_STORAGE en tiempo de ejecución no tiene efectos en la sesión actual

Esto se refiere al nuevo modelo de permisos de tiempo de ejecución introducido en Android Marshmallow al solicitar el permiso Manifest.permission.WRITE_EXTERNAL_STORAGE .

En resumen, lo que estoy experimentando es que si solicito (y el usuario permite) el permiso Manifest.permission.WRITE_EXTERNAL_STORAGE , la aplicación no podrá leer y escribir desde el directorio de almacenamiento externo hasta que destruya y reinicie la aplicación .

Esto es lo que estoy haciendo / experimentando:

Mi aplicación empieza en un estado en el que:

 ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED 

Esto es, no tengo permisos para acceder al almacenamiento externo.

Entonces, solicito permiso para Manifest.permission.WRITE_EXTERNAL_STORAGE tal como Google explica

 private void requestWriteExternalStoragePermission() { // Should we show an explanation? if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { new AlertDialog.Builder(this) .setTitle("Inform and request") .setMessage("You need to enable permissions, bla bla bla") .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { ActivityCompat.requestPermissions(MendeleyActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RC_PERMISSION_WRITE_EXTERNAL_STORAGE); } }) .show(); } else { ActivityCompat.requestPermissions(MendeleyActivity.this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, RC_PERMISSION_WRITE_EXTERNAL_STORAGE); } } 

Una vez que el usuario permite el permiso, se invoca onRequestPermissionsResult .

 @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case RC_PERMISSION_WRITE_EXTERNAL_STORAGE: { // If request is cancelled, the result arrays are empty. if (grantResults.length > 0 && PackageManager.PERMISSION_GRANTED // allowed } else { // denied } break; } } } 

El bloque allowed se ejecuta, confirmando que el usuario ha concedido permisos.

Inmediatamente después de esto, si no destruyo y vuelvo a abrir la aplicación, todavía no tengo permiso de acceso al almacenamiento externo. Más específicamente:

 hasWriteExternalStoragePermission(); // returns true Environment.getExternalStorageDirectory().canRead(); // RETURNS FALSE!! Environment.getExternalStorageDirectory().canWrite(); // RETURNS FALSE!! 

Por lo tanto, parece que el tiempo de ejecución de Android piensa que tengo permisos, pero el sistema de archivos no … De hecho, tratando de acceder a Environment.getExternalStorageDirectory() lanza la excepción:

 android.system.ErrnoException: open failed: EACCES (Permission denied) at libcore.io.Posix.open(Native Method) at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186) at libcore.io.IoBridge.open(IoBridge.java:438) at java.io.FileOutputStream.<init>(FileOutputStream.java:87) at java.io.FileOutputStream.<init>(FileOutputStream.java:72) 

Si ahora destruyo la aplicación y la vuelvo a abrir , el comportamiento se vuelve como debería, pudiendo leer y escribir en la carpeta de almacenamiento externo.

¿Alguien está experimentando esto?

Estoy usando un emulador oficial con:

  • Últimas API de Android 6.0 (API 23) 23, Rev 1.
  • Emulador que ejecuta Intel x86 Atom System Image, API 23, Rev 1.

Construyo la aplicación con:

 android { compileSdkVersion 23 buildToolsVersion "22.0.1" defaultConfig { minSdkVersion 16 targetSdkVersion 23 } ... } 

Si alguien confirma esto y yo no soy el único, supongo que tendremos que abrir un error, pero espero que esté haciendo algo mal, ya que creo que una característica de este tipo es poco probable que sea buggy en el SDK.

http://developer.android.com/reference/android/Manifest.permission.html#WRITE_EXTERNAL_STORAGE :

A partir del nivel 19 de la API, este permiso no es necesario para leer / escribir archivos en los directorios específicos de la aplicación devueltos por getExternalFilesDir (String) y getExternalCacheDir ().

"La solicitud de permisos de tiempo de ejecución" comienza en el nivel 23 de la API, obviamente por encima de 19, por lo que el permiso ya no es necesario, a menos que accedas a los datos fuera de la carpeta señalada por getExternalFilesDir (). Así que creo que esto es un error del emulador.

En los objetivos inferiores por debajo del nivel 19, que no admiten el permiso solicitante en tiempo de ejecución, sólo calim el permiso en manifiesto y funcionará.

Yo tuve el mismo problema. Resulta que esto parece ser un problema más grande. Cambiar el permiso para escribir en el almacenamiento externo cambia el GID para este proceso (en el lado de linux). Para cambiar el ID, el proceso debe ser reiniciado. La próxima vez que abra la aplicación, se establecerá el nuevo ID de grupo y se concederá el permiso.

Larga historia corta, me temo que esto no es un error en el emulador, pero de hecho un problema más grande con Linux y Android.

He "resuelto" esto pidiendo permiso la primera vez que se ejecuta la aplicación y reiniciarla cuando el permiso se da así:

 PackageManager packageManager = getPackageManager(); Intent intent = packageManager.getLaunchIntentForPackage(getPackageName()); ComponentName componentName = intent.getComponent(); Intent mainIntent = IntentCompat.makeRestartActivityTask(componentName); startActivity(mainIntent); System.exit(0); 

Puede intentar crear un servicio que se ejecute en segundo plano (con otro id de proceso) y darle el permiso. De esta manera sólo tendría que reiniciar el servicio y no la aplicación completa. En el lado de abajo esto podría hacer más trabajo para usted.

Espero que esto ayude.

— EDITAR —

El usuario M66B ( https://stackoverflow.com/a/32473449/1565635 ) encontró una lista de los gids relacionados. Puede encontrar más información en: https://android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml

FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.