RudyF
RudyF

Reputation: 825

Android 6.0 Marshmallow. Cannot write to SD Card

I have an app that uses external storage to store photographs. As required, in its manifest, the following permissions are requested

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

and it uses the following to retrieve the required directory

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

SimpleDateFormat dateFormat = new SimpleDateFormat("MM-dd", Locale.US);
String date = dateFormat.format(new Date());
storageDir = new File(sdDir, getResources().getString(
            R.string.storagedir)
            + "-" + date);

// Create directory, error handling
if (!storageDir.exists() && !storageDir.mkdirs()) {
 ... fails here

The app works fine on Android 5.1 to 2.3; it has been on Google Play for over a year.

Following an upgrade of one of my testing phones (Android One) to 6, it's now returning an error when trying to create the requisite directory, "/sdcard/Pictures/myapp-yy-mm".

The sd card is configured as "Portable storage". I've formatted the sd card. I've replaced it. I've rebooted. All to no avail.

Also, the built-in android screenshot functionality (via Power+Lower volume) is failing "due to limited storage space, or it isn't allowed by the app or your organisation".

Any ideas?

Upvotes: 52

Views: 137540

Answers (5)

icaksama
icaksama

Reputation: 782

Maybe you cannot use manifest class from generated code in your project. So, you can use manifest class from android sdk "android.Manifest.permission.WRITE_EXTERNAL_STORAGE". But in Marsmallow version have 2 permission must grant are WRITE and READ EXTERNAL STORAGE in storage category. See my program, my program will request permission until user choose yes and do something after permissions is granted.

            if (Build.VERSION.SDK_INT >= 23) {
                if (ContextCompat.checkSelfPermission(LoginActivity.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(LoginActivity.this, android.Manifest.permission.READ_EXTERNAL_STORAGE)
                        != PackageManager.PERMISSION_GRANTED) {
                    ActivityCompat.requestPermissions(LoginActivity.this,
                            new String[]{android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE},
                            1);
                } else {
                    //do something
                }
            } else {
                    //do something
            }

Upvotes: 6

Stefano D
Stefano D

Reputation: 958

Android Documentation on Manifest.permission.Manifest.permission.WRITE_EXTERNAL_STORAGE states:

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().


I think that this means you do not have to code for the run-time implementation of the WRITE_EXTERNAL_STORAGE permission unless the app is writing to a directory that is not specific to your app.

You can define the max sdk version in the manifest per permission like:

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

Also make sure to change the target SDK in the build.graddle and not the manifest, the gradle settings will always overwrite the manifest settings.

android {
compileSdkVersion 23
buildToolsVersion '23.0.1'
defaultConfig {
    minSdkVersion 17
    targetSdkVersion 22
}

Upvotes: 3

Dũng Trần Trung
Dũng Trần Trung

Reputation: 6276

I faced the same problem. There are two types of permissions in Android:

  • Dangerous (access to contacts, write to external storage...)
  • Normal

Normal permissions are automatically approved by Android while dangerous permissions need to be approved by Android users.

Here is the strategy to get dangerous permissions in Android 6.0

  1. Check if you have the permission granted
  2. If your app is already granted the permission, go ahead and perform normally.
  3. If your app doesn't have the permission yet, ask for user to approve
  4. Listen to user approval in onRequestPermissionsResult

Here is my case: I need to write to external storage.

First, I check if I have the permission:

...
private static final int REQUEST_WRITE_STORAGE = 112;
...
boolean hasPermission = (ContextCompat.checkSelfPermission(activity,
            Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
if (!hasPermission) {
    ActivityCompat.requestPermissions(parentActivity,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                REQUEST_WRITE_STORAGE);
}

Then check the user's approval:

@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode)
    {
        case REQUEST_WRITE_STORAGE: {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED)
            {
                //reload my activity with permission granted or use the features what required the permission
            } else
            {
                Toast.makeText(parentActivity, "The app was not allowed to write to your storage. Hence, it cannot function properly. Please consider granting it this permission", Toast.LENGTH_LONG).show();
            }
        }
    }

}

You can read more about the new permission model here: https://developer.android.com/training/permissions/requesting.html

Upvotes: 51

codeFreak
codeFreak

Reputation: 353

Android changed how permissions work with Android 6.0 that's the reason for your errors. You have to actually request and check if the permission was granted by user to use. So permissions in manifest file will only work for api below 21. Check this link for a snippet of how permissions are requested in api23 http://android-developers.blogspot.nl/2015/09/google-play-services-81-and-android-60.html?m=1

Code:-

If (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) !=
                PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_RC);
            return;
        }`


` @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == STORAGE_PERMISSION_RC) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                //permission granted  start reading
            } else {
                Toast.makeText(this, "No permission to read external storage.", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

Upvotes: 7

RudyF
RudyF

Reputation: 825

Right. So I've finally got to the bottom of the problem: it was a botched in-place OTA upgrade.

My suspicions intensified after my Garmin Fenix 2 wasn't able to connect via bluetooth and after googling "Marshmallow upgrade issues". Anyway, a "Factory reset" fixed the issue.

Surprisingly, the reset did not return the phone to the original Kitkat; instead, the wipe process picked up the OTA downloaded 6.0 upgrade package and ran with it, resulting (I guess) in a "cleaner" upgrade.

Of course, this meant that the phone lost all the apps that I'd installed. But, freshly installed apps, including mine, work without any changes (i.e. there is backward compatibility). Whew!

Upvotes: 3

Related Questions