Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android 9.+ API 29: /storage/emulated/0/Pictures/myPic.png open failed: EACCES (Permission Denied)

I am trying the source code of a very basic camera app on Android Studio 3.4.1 with Android Virtual Device (AVD) Nexus 7 (2012) API 29. I took a picture with the camera in the emulator and tried to save it as /storage/emulated/0/Pictures/myPic.png, but got an exception of /storage/emulated/0/Pictures/myPic.png open failed: EACCES (Permission Denied). I have tried all advice available online, for instance:

  1. In AndroidManifest.xml, I have added:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

  1. In MainActivity.java, I have added the following run-time permission check and request code:
public class MainActivity extends Activity {

    private static final int REQUEST_EXTERNAL_STORAGE = 1;
    private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };

    public static void verifyStoragePermissions(Activity activity) {
        int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);

        boolean checkPermission = (permission == PackageManager.PERMISSION_GRANTED);

        Toast toastPermission = Toast.makeText(activity,
                "Is permission granted? " + checkPermission,
                Toast.LENGTH_SHORT);
        LinearLayout toastLayoutPermission = (LinearLayout) toastPermission.getView();
        TextView toastTVPermission = (TextView) toastLayoutPermission.getChildAt(0);
        toastTVPermission.setTextSize(30);
        toastPermission.show();


        if (permission != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(
                    activity,
                    PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE
            );
        }
    }

  1. I then check and request permission in both onCreate and just before I wanted to save the picture as follows:
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        verifyStoragePermissions(this);


......


private void saveImageToFile(File file)
    {
        if (mCameraBitmap != null)
        {
            FileOutputStream outStream = null;

            verifyStoragePermissions(this);

            try
            {
                outStream = new FileOutputStream(file);

                ...

            }
            catch (Exception e)
            {
                Toast toastException = Toast.makeText(MainActivity.this,
                        "Exception: " + e.getMessage(),
                        Toast.LENGTH_SHORT);
                LinearLayout toastLayoutException = (LinearLayout) toastException.getView();
                TextView toastTVException = (TextView) toastLayoutException.getChildAt(0);
                toastTVException.setTextSize(30);
                toastException.show();
            }

...

  1. I also checked the Storage Permission manually in the emulator to make sure the access to the Storage was allowed.

However, NONE of the advice works!

The code threw an exception of

/storage/emulated/0/Pictures/myPic.png open failed: EACCES (Permission Denied)

when it reached the line of

outStream = new FileOutputStream(file);

Any idea to further solve this? Thanks a lot.

like image 403
lofty Avatar asked Jul 11 '19 15:07

lofty


4 Answers

Your problem is due to the introduction of scoped storage in API 29. You have three solutions to the problem (some have already been mentioned, but I thought it would help to clarify and consolidate):

  1. Only run your app on devices running API 28 and below.

  2. Add android:requestLegacyExternalStorage="true" to AndroidManifest.xml so your app can run on API 29 and above:

    <application android:requestLegacyExternalStorage="true" ...

  3. Modernize your app for API 29+. Start by getting rid of WRITE_EXTERNAL_STORAGE permissions. And replace the call to deprecated Environment.getExternalStoragePublicDirectory() with getExternalFilesDir(), which will save your image on external storage but in a directory only your app can access.

Below is a method to save a Bitmap as a JPG using the MediaStore, which allows other apps to access the image:

private void saveImage(Bitmap bitmap) throws IOException {
    ContentValues contentValues = new ContentValues();
    contentValues.put(MediaStore.MediaColumns.DISPLAY_NAME, "myImage.jpg");
    contentValues.put(MediaStore.MediaColumns.MIME_TYPE, "image/jpeg");
    contentValues.put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES);

    ContentResolver resolver = getContentResolver();
    Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
    OutputStream imageOutStream = null;

    try {
        if (uri == null) {
            throw new IOException("Failed to insert MediaStore row");
        }

        imageOutStream = resolver.openOutputStream(uri);
        if (!bitmap.compress(Bitmap.CompressFormat.JPEG, 100, imageOutStream)) {
            throw new IOException("Failed to compress bitmap");
        }
    }
    finally {
        if (imageOutStream != null) {
            imageOutStream.close();
        }
    }
}
like image 75
tronman Avatar answered Nov 19 '22 12:11

tronman


Adding android:requestLegacyExternalStorage="true" to the Android Manifest worked for me with Android 29

<application
    android:name=".MyApplication"
    android:allowBackup="true"
    android:requestLegacyExternalStorage="true" ...
like image 25
SammyT Avatar answered Nov 19 '22 12:11

SammyT


I also got this error in my project.

I fixed it by adding

android:requestLegacyExternalStorage="true"

in the manifests file. (see here)

like image 37
rupeshraj kumar Avatar answered Nov 19 '22 11:11

rupeshraj kumar


I also tried to use a camera and save the photo to ImageView and had problems with permissions. I changed API from 29 to 28 - didn't help, add

android:requestLegacyExternalStorage="true"

didn't help. I changed the method which I used to create a file to store the image from:

File storageDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);

to:

File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);

And the problem gone. BTW I don't know why. Maybe because the first one was deprecated.

like image 4
Mateusz Jurkiewicz Avatar answered Nov 19 '22 13:11

Mateusz Jurkiewicz