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.