Requesting and allowing WRITE_EXTERNAL_STORAGE permission at runtime has no effects on the current session

31,741

Solution 1

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

Starting in API level 19, this permission is not required to read/write files in your application-specific directories returned by getExternalFilesDir(String) and getExternalCacheDir().

Runtime permissions start at API level 23, obviously above 19, so the permission is not required anymore unless you're accessing the data outside of the folder pointed by getExternalFilesDir(). Therefore, I believe this is a bug only present when testing on an emulator.

On targets below level 19, which don't support requesting permission at runtime, just claim the permission in manifest and it will work.

Solution 2

I had the same problem. Turns out this seems to be a bigger issue. Changing the permission to write to the external storage changes the GID for this process (on the linux side). In order to change the ID the process has to be restarted. Next time you open the app, the new groupID is set and the permission is granted.

Long story short, I'm afraid this is not a bug in the emulator but in fact a bigger issue with Linux and Android.

I "solved" this by asking for permission the first time the app is executed and restarting it when the permission is given like this:

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

You may try to create a service running in the background (having another process id) and giving it the permission. That way you would only need to restart the service and not the complete app. On the down side this might make more work for you.

Hope this helps.

--- EDIT ---

The user M66B (https://stackoverflow.com/a/32473449/1565635) found a list of the related gids. Further information can be found here: https://android.googlesource.com/platform/frameworks/base/+/master/data/etc/platform.xml

Share:
31,741

Related videos on Youtube

GaRRaPeTa
Author by

GaRRaPeTa

I am a software developer originally from Madrid writing Android applications in London. I like coding, painting, Pink Floyd, djing and techno music. Happy coding :-)

Updated on July 11, 2022

Comments

  • GaRRaPeTa
    GaRRaPeTa almost 2 years

    this is regarding the new model of runtime permissions introduced in Android Marshmallow when requesting Manifest.permission.WRITE_EXTERNAL_STORAGE permission.

    In short, what I am experiencing is that if I request (and the user allows) Manifest.permission.WRITE_EXTERNAL_STORAGE permission, the app won't be able to read and write from the external storage directory until I destroy and restart the app.

    This is what I am doing/experiencing:

    My app starts from a state where:

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

    This is, I don't have permissions to to access external storage.

    Then, I request permission to Manifest.permission.WRITE_EXTERNAL_STORAGE just as Google explains

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

    Once the user allows the permission, onRequestPermissionsResult gets invoked.

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

    The allowed block is executed, confirming the user has granted permissions.

    Immediately after this, if I don't destroy and open the app again, I still have no access permission to external storage. More specifically:

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

    So, it seems the Android runtime thinks I have permissions, but the file system doesn't... Indeed, trying to access Environment.getExternalStorageDirectory() throws the 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) 
    

    If I now destroy the app and open it again, the behaviour becomes as it should, being able to read and write in the external storage folder.

    Is anyone experiencing this?

    I am using one official emulator with:

    • Latest Android 6.0 (API 23) API 23, Rev 1.
    • Emulator running Intel x86 Atom System Image, API 23, Rev 1.

    I build the app with:

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

    If someone confirms this and I am not the only one I guess we'll need to open a bug, but I hope I am doing something wrong, as I think such a core feature is unlikely to be buggy in the SDK.

    • CommonsWare
      CommonsWare over 8 years
      "Is anyone experiencing this?" -- I'm not, but I haven't tried it on an emulator. I have been using hardware.
    • GaRRaPeTa
      GaRRaPeTa over 8 years
      Then it's likely to be just on the emulator... I'll update once I receive the Android M update.
    • greywolf82
      greywolf82 over 8 years
    • GaRRaPeTa
      GaRRaPeTa over 8 years
      Thx greywolf82. I failed to find it in my search.
    • damax
      damax about 8 years
      @GaRRaPeTa I have the same issue now with the same symptoms. Have you managed this in other way ?
    • Alberson Melo
      Alberson Melo about 8 years
      I'm experiencing this same problem. Did you solve this anyway?
    • Paraneetharan Saravanaperumal
      Paraneetharan Saravanaperumal almost 8 years
      I also faced same issue in the emulator , anyone solve this issue without restart the app
    • Paraneetharan Saravanaperumal
      Paraneetharan Saravanaperumal almost 8 years
      may be problem in the emulator. anybody confirm this issue in the real device ? @GaRRaPeTa
    • dinchy87
      dinchy87 over 6 years
      Same here, and even on my real device, a Samsung Galaxy S7 Edge with Android 7.0. Is there any solution for this problem? Still cannot write a file to storage because it gets the permission denied exception
  • sven
    sven over 8 years
    This is only half the truth. Unfortunately some devices on Lollipop still require the specific permissions, so that you should avoid removing the permission on API level 19 and above (I did so and now I have to reintroduce it). See stackoverflow.com/questions/27016647/…
  • crazii
    crazii over 8 years
    @sven true. But according to the doc, I still consider it as a bug, even though I am requesting storage permission in practice.
  • sven
    sven over 8 years
    @crazil: 100% agree. Just watend to point this out here to avoid that other developers remove the permission just to recognize that they will need to add it again...
  • Tobias Reich
    Tobias Reich almost 8 years
    This is not a but. See my answer. The new permission system changes the group ID on the linux side which requires a restart of the process.
  • crazii
    crazii almost 8 years
    @TobiasReich No, you missed my point. According to the doc, for API level 19 and above, run time permission is not needed to access getExternalFilesDir(), because access getExternalFilesDir() doesn't need WRITE_EXTERNAL_STORAGE permission. I consider it as a bug because this behavior is different from the doc, or the doc is unclear. At least they should change the doc so that it doesn't confuses any more. I don't know if they've changed it already or not.
  • user755499
    user755499 over 7 years
    Yes, i guess this works and should be accepted answer
  • Tobias Reich
    Tobias Reich over 7 years
    Glad to hear that! :)
  • CommonsWare
    CommonsWare over 7 years
    Please provide a sample project that demonstrates this problem. Then, consider filing an issue. I see no issue report tied to this, and I have never seen this behavior, across a fair number of apps. If the problem is limited to canRead()/canWrite(), I can see perhaps there being an issue. But, for actually reading and writing, you do not need to restart the process to have the granted permission take effect.