web-dev-qa-db-fra.com

La demande et l'autorisation de l'autorisation WRITE_EXTERNAL_STORAGE lors de l'exécution n'a aucun effet sur la session en cours

il s'agit du nouveau modèle de autorisations d'exécution introduites dans Android Marshmallow lors de la demande de l'autorisation Manifest.permission.WRITE_EXTERNAL_STORAGE.

En bref, ce que je vis, c'est que si je demande (et que l'utilisateur autorise) l'autorisation Manifest.permission.WRITE_EXTERNAL_STORAGE, L'application ne pourra pas lire et écrire à partir du répertoire de stockage externe tant que je n'aurai pas détruit et redémarrez l'application.

Voici ce que je fais/expérimente:

Mon application démarre à partir d'un état où:

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

Autrement dit, je n'ai pas l'autorisation d'accéder au stockage externe.

Ensuite, je demande la permission de Manifest.permission.WRITE_EXTERNAL_STORAGE comme Google l'explique

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

Une fois que l'utilisateur autorise l'autorisation, onRequestPermissionsResult est invoqué.

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

Le bloc allowed est exécuté, confirmant que l'utilisateur a accordé des autorisations.

Immédiatement après cela, si je ne détruis pas et ne rouvre pas l'application, je n'ai toujours pas d'autorisation d'accès au stockage externe. Plus précisement:

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

Donc, il semble que le runtime Android pense que j'ai des autorisations, mais le système de fichiers ne le fait pas ... En effet, essayer d'accéder à Environment.getExternalStorageDirectory() lève l'exception:

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 je détruis maintenant l'application et l'ouvre à nouveau , le comportement devient comme il se doit, pouvant lire et écrire dans le dossier de stockage externe.

Quelqu'un vit-il cela?

J'utilise un émulateur officiel avec:

  • Dernière Android 6.0 (API 23) API 23, Rev 1.
  • Émulateur exécutant Intel x86 Atom System Image, API 23, Rev 1.

Je crée l'application avec:

Android {
    compileSdkVersion 23
    buildToolsVersion "22.0.1"

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 23
    }
...
}

Si quelqu'un le confirme et que je ne suis pas le seul, je suppose que nous devrons ouvrir un bogue, mais j'espère que je fais quelque chose de mal, car je pense qu'une telle fonctionnalité de base est peu susceptible d'être boguée dans le SDK.

25
GaRRaPeTa

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

À partir du niveau 19 de l'API, cette autorisation n'est pas requise pour lire/écrire des fichiers dans vos répertoires spécifiques à l'application retournés par getExternalFilesDir (String) et getExternalCacheDir ().

La "demande d'autorisation d'exécution" commence au niveau de l'API 23, évidemment au-dessus de 19, donc l'autorisation n'est plus requise, sauf si vous accédez aux données en dehors du dossier pointé par getExternalFilesDir (). Je pense donc que c'est un bug de l'émulateur.

Sur les cibles inférieures en dessous du niveau 19, qui ne prennent pas en charge la demande d'autorisation lors de l'exécution, calez simplement l'autorisation dans le manifeste et cela fonctionnera.

7
crazii

J'ai eu le même problème. Il s'avère que cela semble être un problème plus important. La modification de l'autorisation d'écrire sur le stockage externe modifie le GID de ce processus (côté Linux). Afin de changer l'ID, le processus doit être redémarré. La prochaine fois que vous ouvrirez l'application, le nouvel ID de groupe est défini et l'autorisation est accordée.

Pour faire court, j'ai bien peur ce n'est pas un bug dans l'émulateur mais en fait un problème plus important avec Linux et Android.

J'ai "résolu" cela en demandant la permission la première fois que l'application est exécutée et en la redémarrant lorsque l'autorisation est donnée comme ceci:

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

Vous pouvez essayer de créer un service exécuté en arrière-plan (avec un autre identifiant de processus) et en lui accordant l'autorisation. De cette façon, vous n'auriez qu'à redémarrer le service et non l'application complète. En revanche, cela pourrait vous faire plus de travail.

J'espère que cela t'aides.

--- MODIFIER ---

L'utilisateur M66B ( https://stackoverflow.com/a/32473449/1565635 ) a trouvé une liste des gids associés. De plus amples informations peuvent être trouvées ici: https://Android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml

5
Tobias Reich