John Ashmore
John Ashmore

Reputation: 1045

Permission denied on writing to external storage despite permission

I have an Android 7.0 test device and my APK targets = "targetSdkVersion 22", with:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

with:

final File f = new 
File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES) + File.separator + "DressUpBaby" + photonumber + ".png");
f.createNewFile();

and at this point I get the warning:

W/System.err: java.io.IOException: Permission denied

How to get it to save the file? This was working when I created this on Eclipse, but now that I have updated and moved to Android Studio it seems to have broken something.

Upvotes: 20

Views: 50410

Answers (8)

Ghazal
Ghazal

Reputation: 383

This article really helped me.

https://ourcodeworld.com/articles/read/1559/how-does-manage-external-storage-permission-work-in-android

1. Request MANAGE_EXTERNAL_STORAGE permission :

<manifest 
    xmlns:android="http://schemas.android.com/apk/res/android" 
    package="com.ourcodeworld.plugins.capacitornativefilepicker"
    xmlns:tools="http://schemas.android.com/tools"
>
    <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_EXTERNAL_STORAGE" tools:ignore="ScopedStorage"/>
</manifest>

2. Request External Storage Permissions

After declaring the permissions, you need to request the regular READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions. Note that we don't include the MANAGE_EXTERNAL_STORAGE, because if you request it, when verifying if you have access or not, the mentioned permission will always return denied, even though the user has already granted access:

ActivityCompat.requestPermissions(
    this,
    new String[]{
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.MANAGE_EXTERNAL_STORAGE
    },
    1
);

3. Checking permission of MANAGE_EXTERNAL_STORAGE

Now, instead of the regular permissions request to handle the mentioned permission, you need instead to verify whether the access to the external storage is allowed. In case it isn't, you need to launch a new activity showing the system's dialog where the user should manually allow the access to the external storage to your app like this:

import android.os.Environment;
import android.content.Intent;
import android.provider.Settings;
import android.net.Uri;

// If you have access to the external storage, do whatever you need
if (Environment.isExternalStorageManager()){

// If you don't have access, launch a new activity to show the user the system's dialog
// to allow access to the external storage
}else{
    Intent intent = new Intent();
    intent.setAction(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
    Uri uri = Uri.fromParts("package", this.getPackageName(), null);
    intent.setData(uri);
    startActivity(intent);
}

So when the user installs the app and try to access the file picker, if the access to all files isn't granted yet, the following system intent will be launched:

enter image description here

The user will have to explicitly allow access to all files if you want to read files from any source in the device and that should be enough.

Upvotes: 2

Suresh B B
Suresh B B

Reputation: 1420

in latest version of android making request permission bit different for example, if you want access storage permission first you need request access permission from Manifest.xml

like:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

and most important don't forget to allow this from application tag of Manifets.xml,

`android:requestLegacyExternalStorage="true"'

//and created method from your activity to request run to permission call this function from activity anywhere required.

private boolean requestPermission(){
        boolean request=true;
        String[] permissions={Manifest.permission.CAMERA,
                                Manifest.permission.ACCESS_FINE_LOCATION,
                                Manifest.permission.READ_EXTERNAL_STORAGE,
                                Manifest.permission.WRITE_EXTERNAL_STORAGE,
                                Manifest.permission.ACCESS_COARSE_LOCATION,
                                Manifest.permission.ACCESS_FINE_LOCATION};
        if (permissions.length!=0){
            ActivityCompat.requestPermissions(this,permissions,102);
            request= true;
        }

        else{
            toastMsg("Permissions Denied");
            println("Permissions not allowed by User...");
            request=false;
        }

        return request;

    }

Upvotes: 1

John Doe
John Doe

Reputation: 1131

UPDATE: See Volker Voecking's answer for a better solution


EDIT: This is simply a workaround for those who don't have time to look for a solution. *

If you get permission denied error even when the permissions are granted and you already implemented permission checks,

make sure you're not targetting api level 29:

Change targetSdkVersion and compilesdkversion from 29 to 28 or any other lower level.

Upvotes: 20

user13782500
user13782500

Reputation:

Related to the answer "make sure you're not targetting api level 29...", I found this.

https://developer.android.com/reference/android/os/Environment#getExternalStorageDirectory

。。。。。。。。。。

"getExternalStoragePublicDirectory"

This method was deprecated in API level 29.

To improve user privacy, direct access to shared/external storage devices is deprecated. When an app targets Build.VERSION_CODES.Q, the path returned from this method is no longer directly accessible to apps. Apps can continue to access content stored on shared/external storage by migrating to alternatives such as Context#getExternalFilesDir(String), MediaStore, or Intent#ACTION_OPEN_DOCUMENT.

。。。。。。。。。。

so, in that case you have to replace "getExternalStoragePublicDirectory" with those other methods.

Upvotes: 1

Volker Voecking
Volker Voecking

Reputation: 5583

If you target SDK 29 and you still get a "permission denied" error after successfully requesting the WRITE_EXTERNAL_STORAGE permission you should add

android:requestLegacyExternalStorage="true"

to the application definition in AndroidManifest.xml.

<manifest ... >
  <!-- This attribute is "false" by default on apps targeting
       Android 10 or higher. -->
    <application android:requestLegacyExternalStorage="true" ... >
      ...
    </application>
</manifest>

see https://developer.android.com/training/data-storage/compatibility

Upvotes: 18

t.m.
t.m.

Reputation: 1490

Here is what I did in a similar situation:

Step 1: Add permission to manifest file

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

Step 2: Create a permission checking function

void checkForPermissions()
{
    // Here, thisActivity is the current activity
    if (ContextCompat.checkSelfPermission(this,
            Manifest.permission.WRITE_EXTERNAL_STORAGE)
            != PackageManager.PERMISSION_GRANTED) {

        // Permission is not granted
        // Should we show an explanation?
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
            // Show an explanation to the user *asynchronously* -- don't block
            // this thread waiting for the user's response! After the user
            // sees the explanation, try again to request the permission.
            // This part I didn't implement,because for my case it isn't needed
            Log.i(TAG,"Unexpected flow");
        } else {
            // No explanation needed; request the permission
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                    MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE);

            // MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE is an
            // app-defined int constant. The callback method gets the
            // result of the request.
        }
    } else {
        // Permission is already granted, call the function that does what you need

        onFileWritePermissionGranted();
    }
}

Step 3: Implement onRequestPermissionsResult

 @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_EXTERNAL_STORAGE: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // permission was granted, yay! 
                    // call the function that does what you need
                    onFileWritePermissionGranted();
                } else {
                    Log.e(TAG, "Write permissions has to be granted tp ATMS, otherwise it cannot operate properly.\n Exiting the program...\n");
                }
                return;
            }

            // other 'case' lines to check for other
            // permissions this app might request.
        }
    }

Step 4: Use the permission :)

void onFileWritePermissionGranted()
{
   // Write to some file
}

Upvotes: 1

Pranay Katta
Pranay Katta

Reputation: 71

Android N introduces a new model of permissions which only asks for permissions when the app really needs it rather than during installation like it previously did.

Use the following code to ask for permissions

Note - Also add the required permissions in the Manifest file

If you aren't asking permissions from Main Activity pass the reference to the context/activity

The following example shows example for asking permissions to Write to external storage, multiple permisisons can be requiested at the same time (Not recommended by google).

public class MainActivity extends AppCompatActivity {
/**
 * Variables for requiesting permissions, API 25+
 */
private int requestCode;
private int grantResults[];


/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {if(ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_DENIED ){
        //if you dont have required permissions ask for it (only required for API 23+)
        ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},requestCode);


        onRequestPermissionsResult(requestCode,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},grantResults);
    }

    @Override // android recommended class to handle permissions
    public void onRequestPermissionsResult(int requestCode,
                                       String permissions[], int[] grantResults) {
        switch (requestCode) {
            case 1: {

            // If request is cancelled, the result arrays are empty.
            if (grantResults.length > 0
                    && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                Log.d("permission","granted");
            } else {

                // permission denied, boo! Disable the
                // functionality that depends on this permission.uujm
                Toast.makeText(MainActivity.this, "Permission denied to read your External storage", Toast.LENGTH_SHORT).show();

                //app cannot function without this permission for now so close it...
                onDestroy();
            }
            return;
        }

        // other 'case' line to check fosr other
        // permissions this app might request
    }
}

Upvotes: 7

fdelafuente
fdelafuente

Reputation: 1122

If you're running your app on API level 23 or greater you have to request permission at runtime.

Request permission:

String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
requestPermissions(permissions, WRITE_REQUEST_CODE);

Then handle the result:

@Override
public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {
    switch (requestCode) {
       case WRITE_REQUEST_CODE:
         if(grantResults[0] == PackageManager.PERMISSION_GRANTED){
           //Granted.


         }
       else{
           //Denied.
         }
        break;
    }
}

For more information visit Requesting Permissions at Run Time - Android Doc

Upvotes: 12

Related Questions