Android 6.0 Marshmallow. Cannot write to SD card

I have an application that uses external storage to store photos. If necessary, the following permissions are requested in your manifest:

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

and to get the required directory

The following is used:
 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 application works fine on Android from 5.1 to 2.3; He’s been on Google Play for over a year now.

After updating one of my test phones (Android One) to 6, it now returns an error when trying to create the required directory "/ sdcard / Pictures / myapp-yy-mm".

The SD card is configured as "portable storage". I formatted the SD card. I replaced it. I rebooted. All to no avail.

In addition, the built-in Android screenshot function (using Power + Lower volume) fails "due to limited storage space or is not allowed by the application or your organization."

Any ideas?

+49
android android-6.0-marshmallow android-sdcard
Oct. 15 '15 at 4:10
source share
6 answers

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

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

Normal permissions are automatically approved by Android, and dangerous permissions must be approved by Android users.

Here's a strategy for getting dangerous permissions in Android 6.0

  • Check if you have permission.
  • If your application has already been granted permission, proceed normally and perform normally.
  • If your application does not yet have permission, ask the user to approve
  • Listen to user statement in onRequestPermissionsResult

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

First, I check if I have 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 statement:

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

Here you can learn more about the new resolution model: https://developer.android.com/training/permissions/requesting.html

+49
Oct 16 '15 at 7:30
source share

First I will give you the Dangerous Permissions List in Android M and later

enter image description here enter image description here

Then give you an example of how to request permission in Android M and later .

I am asking the user for permission WRITE_EXTERNAL_STORAGE .

First add permission to the Android manifest file.

Step 1 Declare Request Code

  private static String TAG = "PermissionDemo"; private static final int REQUEST_WRITE_STORAGE = 112; 

Step 2 Add this code if you want the user to request permission

  //ask for the permission in android M int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE); if (permission != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "Permission to record denied"); if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setMessage("Permission to access the SD-CARD is required for this app to Download PDF.") .setTitle("Permission required"); builder.setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { Log.i(TAG, "Clicked"); makeRequest(); } }); AlertDialog dialog = builder.create(); dialog.show(); } else { makeRequest(); } } protected void makeRequest() { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE); } 

Step 3 Add Override Method for Request

  @Override public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) { switch (requestCode) { case REQUEST_WRITE_STORAGE: { if (grantResults.length == 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "Permission has been denied by user"); } else { Log.i(TAG, "Permission has been granted by user"); } return; } } } 

Note. Remember to add permission to the manifest file

BEST EXAMPLE BELOW WITH MULTIPLE RESOLUTION PLUS COVERES ALL SCENARIO

I added comments so you can easily understand.

 import android.Manifest; import android.content.DialogInterface; import android.content.Intent; import android.content.pm.PackageManager; import android.net.Uri; import android.provider.Settings; import android.support.annotation.NonNull; import android.support.v4.app.ActivityCompat; import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.Toast; import com.production.hometech.busycoder.R; import java.util.ArrayList; public class PermissionInActivity extends AppCompatActivity implements View.OnClickListener { private static final int REQUEST_PERMISSION_SETTING = 99; private Button bt_camera; private static final String[] PARAMS_TAKE_PHOTO = { Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE }; private static final int RESULT_PARAMS_TAKE_PHOTO = 11; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_permission_in); bt_camera = (Button) findViewById(R.id.bt_camera); bt_camera.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.bt_camera: takePhoto(); break; } } /** * shouldShowRequestPermissionRationale() = This will return true if the user had previously declined to grant you permission * NOTE : that ActivityCompat also has a backwards-compatible implementation of * shouldShowRequestPermissionRationale(), so you can avoid your own API level * checks. * <p> * shouldShowRequestPermissionRationale() = returns false if the user declined the permission and checked the checkbox to ask you to stop pestering the * user. * <p> * requestPermissions() = request for the permisssiion */ private void takePhoto() { if (canTakePhoto()) { Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show(); } else if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)) { Toast.makeText(this, "You should give permission", Toast.LENGTH_SHORT).show(); ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO); } else { ActivityCompat.requestPermissions(this, netPermisssion(PARAMS_TAKE_PHOTO), RESULT_PARAMS_TAKE_PHOTO); } } // This method return permission denied String[] so we can request again private String[] netPermisssion(String[] wantedPermissions) { ArrayList<String> result = new ArrayList<>(); for (String permission : wantedPermissions) { if (!hasPermission(permission)) { result.add(permission); } } return (result.toArray(new String[result.size()])); } private boolean canTakePhoto() { return (hasPermission(Manifest.permission.CAMERA) && hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)); } /** * checkSelfPermission() = you can check if you have been granted a runtime permission or not * ex = ContextCompat.checkSelfPermission(this,permissionString)== PackageManager.PERMISSION_GRANTED * <p> * ContextCompat offers a backwards-compatible implementation of checkSelfPermission(), ActivityCompat offers a backwards-compatible * implementation of requestPermissions() that you can use. * * @param permissionString * @return */ private boolean hasPermission(String permissionString) { return (ContextCompat.checkSelfPermission(this, permissionString) == PackageManager.PERMISSION_GRANTED); } /** * requestPermissions() action goes to onRequestPermissionsResult() whether user can GARNT or DENIED those permisssions * * @param requestCode * @param permissions * @param grantResults */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == RESULT_PARAMS_TAKE_PHOTO) { if (canTakePhoto()) { Toast.makeText(this, "You can take picture", Toast.LENGTH_SHORT).show(); } else if (!(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_EXTERNAL_STORAGE))) { final AlertDialog.Builder settingDialog = new AlertDialog.Builder(PermissionInActivity.this); settingDialog.setTitle("Permissioin"); settingDialog.setMessage("Now you need to enable permisssion from the setting because without permission this app won't run properly \n\n goto -> setting -> appInfo"); settingDialog.setCancelable(false); settingDialog.setPositiveButton("Setting", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.cancel(); Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); Uri uri = Uri.fromParts("package", getPackageName(), null); intent.setData(uri); startActivityForResult(intent, REQUEST_PERMISSION_SETTING); Toast.makeText(getBaseContext(), "Go to Permissions to Grant all permission ENABLE", Toast.LENGTH_LONG).show(); } }); settingDialog.show(); Toast.makeText(this, "You need to grant permission from setting", Toast.LENGTH_SHORT).show(); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (requestCode == REQUEST_PERMISSION_SETTING) { if (canTakePhoto()) { Toast.makeText(this, "You can take PHOTO", Toast.LENGTH_SHORT).show(); } } } } 

Special case of configuration changes

It is possible that the user will rotate the device or otherwise initiate a configuration change, while our dialogue dialog is in the foreground. Since our activity is still visible behind this dialogue, we are destroyed and recreated ... but we do not want to raise the rights dialog again.

That's why we have a logical name called isInPermission that keeps track of whether or not we are in the middle of a permission request. We keep this value in onSaveInstanceState() :

 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(STATE_IN_PERMISSION, isInPermission); } 

We restore it to onCreate() . If we do not have all the required permissions, but isInPermission is true, we skip the permission request because we are already in the middle of it.

+19
May 21 '16 at 4:23
source share

Android has changed how permissions work with Android 6.0, which is the cause of your errors. You must truly request and verify whether the permission was granted by the user for use. Thus, permissions in the 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

The 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(); } } } } 
+7
Oct. 15 '15 at 13:26
source share

Perhaps you cannot use the manifest class from the generated code in your project. So you can use the manifest class from android sdk "android.Manifest.permission.WRITE_EXTERNAL_STORAGE". But in the Marsmallow version there are 2 permissions that must be granted: WRITE and READ EXTERNAL STORAGE in the storage category. Look at my program, my program will ask for permission until the user selects “yes” and does something after granting permissions.

  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 } 
+6
Mar 30 '16 at 5:49
source share

Right So I finally got to the bottom of the problem: it was a failed OTA update in place.

My suspicions intensified after my Garmin Fenix ​​2 was unable to connect via bluetooth and after a Google search for “Marshmallow update problems”. Anyway, "Factory reset" fixed the problem.

Surprisingly, reset did not return the phone to the original Kitkat; instead, the cleanup process cleared the downloaded OTA 6.0 service pack and worked with it, resulting in (I think) a “cleaner” update.

Of course, this meant that the phone lost all the applications that I installed. But recently installed applications, including mine, work without any changes (i.e. there is backward compatibility). Phew!

+3
Oct 18 '15 at 6:08
source share

Android documentation at Manifest.permission.Manifest.permission.WRITE_EXTERNAL_STORAGE:

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




I think this means that you do not need to code the implementation at runtime with the WRITE_EXTERNAL_STORAGE permission if the application does not write to a directory that is not related to your application.

You can specify the maximum version of sdk in the manifest for permission, for example:

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

Also, do not forget to change the target SDK in build.graddle, not the manifest, gradle parameters will always overwrite manifest parameters.

 android { compileSdkVersion 23 buildToolsVersion '23.0.1' defaultConfig { minSdkVersion 17 targetSdkVersion 22 } 
+3
Jan 29 '16 at 18:08
source share



All Articles