Android Storage Permission Handling Across API Levels

Android Permission Categories

Android permissions are divided into three distinct types:

  • Normal permissions: Declared in the manifest file only
  • Dangerous permissions: Requested at runtime via system dialogs
  • Special permissions: Requested at runtime via system Activity redirects

Storage permissions underwent significant changes. After Android 6.0 (API 23), storage became a dangerous permission. In Android 11 (API 30), it was upgraded to a special permission. The key difference lies in the request mechanism: dangerous permissions use dialogs while special permissions launch dedicated system Activities.

Storage Permission Implementation Below Android 10

Updating Build Configuration

android {
    defaultConfig {
        targetSdkVersion 23
    }
}

Manifest Declaration

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

Runtime Permission Request

public class StoragePermissionActivity extends AppCompatActivity {

    private static final int PERMISSION_REQUEST_CODE = 2001;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        checkAndRequestStoragePermission();
    }

    private void checkAndRequestStoragePermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            boolean readGranted = ContextCompat.checkSelfPermission(this, 
                Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
            boolean writeGranted = ContextCompat.checkSelfPermission(this, 
                Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
            
            if (readGranted && writeGranted) {
                performFileWriteOperation();
            } else {
                ActivityCompat.requestPermissions(this,
                    new String[]{
                        Manifest.permission.READ_EXTERNAL_STORAGE,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE
                    },
                    PERMISSION_REQUEST_CODE);
            }
        } else {
            performFileWriteOperation();
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        
        if (requestCode == PERMISSION_REQUEST_CODE) {
            boolean readGranted = ContextCompat.checkSelfPermission(this, 
                Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
            boolean writeGranted = ContextCompat.checkSelfPermission(this, 
                Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
            
            if (readGranted && writeGranted) {
                performFileWriteOperation();
            } else {
                showMessage("Storage permission denied");
            }
        }
    }

    private void performFileWriteOperation() {
        showMessage("File write operation ready");
    }

    private void showMessage(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}

Android 10 Compatibility Flag

For applications targeting API 29 and above, add the following to the manifest:

<application
    android:requestLegacyExternalStorage="true">

This flag is required to maintain backward compatibility with etxernal storage access on Android 10 devices.

Storage Permission Implementation for Android 11 and Above

Updating Build Configuration

android {
    defaultConfig {
        targetSdkVersion 30
    }
}

Manifest Declaration

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

Runtime Permission Request

public class StoragePermissionActivity extends AppCompatActivity {

    private static final int STORAGE_ACCESS_REQUEST = 2002;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initiateStoragePermissionFlow();
    }

    private void initiateStoragePermissionFlow() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // Android 11 and above: special permission flow
            if (Environment.isExternalStorageManager()) {
                performFileWriteOperation();
            } else {
                navigateToPermissionSettings();
            }
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // Android 6.0 to Android 10: dangerous permission flow
            boolean hasReadPermission = ContextCompat.checkSelfPermission(this, 
                Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
            boolean hasWritePermission = ContextCompat.checkSelfPermission(this, 
                Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
            
            if (hasReadPermission && hasWritePermission) {
                performFileWriteOperation();
            } else {
                ActivityCompat.requestPermissions(this,
                    new String[]{
                        Manifest.permission.READ_EXTERNAL_STORAGE,
                        Manifest.permission.WRITE_EXTERNAL_STORAGE
                    },
                    STORAGE_ACCESS_REQUEST);
            }
        } else {
            // Pre-Marshmallow: permissions granted at install time
            performFileWriteOperation();
        }
    }

    private void navigateToPermissionSettings() {
        Intent permissionIntent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
        permissionIntent.setData(Uri.parse("package:" + getPackageName()));
        startActivityForResult(permissionIntent, STORAGE_ACCESS_REQUEST);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, 
                                           @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        
        if (requestCode == STORAGE_ACCESS_REQUEST) {
            boolean hasReadPermission = ContextCompat.checkSelfPermission(this, 
                Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
            boolean hasWritePermission = ContextCompat.checkSelfPermission(this, 
                Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
            
            if (hasReadPermission && hasWritePermission) {
                performFileWriteOperation();
            } else {
                showMessage("Storage permission denied");
            }
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        
        if (requestCode == STORAGE_ACCESS_REQUEST && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            if (Environment.isExternalStorageManager()) {
                performFileWriteOperation();
            } else {
                showMessage("All-files access permission not granted");
            }
        }
    }

    private void performFileWriteOperation() {
        showMessage("Ready for file operations");
    }

    private void showMessage(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }
}

Using Third-Party Permission Library

XXPermissions.with(this)
    .permission(Permission.MANAGE_EXTERNAL_STORAGE)
    .request(new OnPermissionCallback() {

        @Override
        public void onGranted(List<String> permissions, boolean allGranted) {
            if (allGranted) {
                displayToast("Storage permission granted");
            }
        }

        @Override
        public void onDenied(List<String> permissions, boolean permanentlyDenied) {
            if (permanentlyDenied) {
                displayToast("Permanently denied - please enable manually");
                XXPermissions.startPermissionActivity(StoragePermissionActivity.this, permissions);
            } else {
                displayToast("Storage permission denied");
            }
        }
    });

The XXPermissions library handles the permission request flow across different Android versions, simplifying the implementation complexity.

Tags: Android Storage Permissions API 30 Android 11 App Development

Posted on Sun, 02 Aug 2026 16:36:27 +0000 by Bubbagump