Skip to content

Android Storage Permissions for API 33+

Problem Statement

Starting with Android 13 (API 33), the traditional WRITE_EXTERNAL_STORAGE permission undergoes significant changes. Developers targeting API 33+ report that the runtime permission dialog for WRITE_EXTERNAL_STORAGE doesn't appear, even when declared in the manifest. This issue arises because:

  • Android 13 deprecates broad storage permissions
  • Google introduced granular media permissions (READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO) for media files only
  • There's confusion about how to handle non-media file operations

Many developers need to save app-specific files (like keystores) but can't find clear documentation for non-media storage scenarios.

Understanding Storage Changes

Key Android 13+ Behavior Changes

  • WRITE_EXTERNAL_STORAGE is fully deprecated: Starting from Android 11 (API 30), requesting this permission provides no additional access
  • Granular media permissions: Only required when accessing photos, videos, or audio
  • Reduced scope: Apps can no longer access arbitrary locations in external storage

Legacy Permissions Are Dead

Attempting to request WRITE_EXTERNAL_STORAGE on Android 13+ is futile - the system will simply ignore the request. Even if granted, it provides no additional file access.

For private files that don't need user visibility (like your keystore file), use app-specific directories that require no permissions:

kt
// Create directory in app-specific external storage
val dir = getExternalFilesDir("keystores")
if (!dir?.exists() == true) {
    if (!dir?.mkdirs() == true) {
        // Handle directory creation error
    }
}

// Create file inside the directory
val keyFile = File(dir, "my_keystore.bin")
keyFile.writeBytes(keystoreData)

Key benefits:

  • Zero permissions required
  • Files are automatically deleted when app is uninstalled
  • Works on all Android versions

Internal vs External Storage

  • getFilesDir(): Internal storage (smaller space, always available)
  • getExternalFilesDir(): External storage (larger space, may be unavailable when mounted)

2. Storage Access Framework for Shared Files

For files that should be visible in system file managers (e.g., exported reports), use Android's document picker:

kt
// Launch directory picker
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, REQUEST_CODE_DIR)

// Handle result
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if (requestCode == REQUEST_CODE_DIR && resultCode == RESULT_OK) {
        data?.data?.let { uri ->
            contentResolver.takePersistableUriPermission(
                uri, 
                Intent.FLAG_GRANT_READ_URI_PERMISSION or
                Intent.FLAG_GRANT_WRITE_URI_PERMISSION
            )
            saveFileToPickedLocation(uri)
        }
    }
}

private fun saveFileToPickedLocation(baseUri: Uri) {
    val dir = DocumentFile.fromTreeUri(context, baseUri)
    val file = dir?.createFile("application/octet-stream", "keystore.bin")
    file?.uri?.let { uri ->
        contentResolver.openOutputStream(uri)?.use { stream ->
            stream.write(keystoreData)
        }
    }
}

3. Media Store for Public Media (Special Cases)

For media files only, use the new granular permissions:

xml
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />

Permission Handling Compatibility

If supporting older Android versions, conditionally request permissions:

kt
fun saveKeystore() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
        // Request legacy permissions on older devices
        requestLegacyStoragePermission()
    } else {
        // Directly save to app-specific storage on API 33+
        saveToAppStorage()
    }
}

private fun requestLegacyStoragePermission() {
    ActivityCompat.requestPermissions(
        this,
        arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
        REQUEST_LEGACY_STORAGE
    )
}

Why These Solutions Work

  1. App-specific storage:

    • Requires no permissions
    • Sandboxes files to your app's private area
    • Follows Android's scoped storage guidelines
  2. Storage Access Framework:

    • User-driven file selection
    • Provides explicit user consent
    • Compatible with all Android versions
  3. Granular permissions:

    • Target specific media types
    • Follow least-privilege principle

Performance Considerations

  • Internal storage (getFilesDir()) is faster but has limited space
  • External storage (getExternalFilesDir()) has more capacity but may be slower

Migration Checklist

  1. Remove WRITE_EXTERNAL_STORAGE from manifest if unused
  2. Replace all legacy storage paths with:
    • Context#getFilesDir()
    • Context#getExternalFilesDir()
    • MediaStore for shared media
  3. Test file operations on Android 10+ devices
  4. For file manager functionality:
    • Target Android 11+ (R)
    • Request MANAGE_EXTERNAL_STORAGE
    • Through Play Store approval

Common Mistakes to Avoid

  1. Requesting deprecated permissions:

    xml
    <!-- DON'T DO THIS -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  2. Using hardcoded paths:

    kt
    // Avoid - won't work on Android 10+
    File("/sdcard/MyApp/keystore.bin")
  3. Ignoring compatibility:

    kt
    // Always check SDK version
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Use scoped storage methods
    } else {
        // Use legacy methods with permissions
    }

Conclusion

Android 13 completes the decade-long storage permission overhaul that began with Android 10. Instead of fighting against WRITE_EXTERNAL_STORAGE:

  1. Use app-specific storage with getExternalFilesDir() for private files
  2. Use Storage Access Framework for user-visible documents
  3. Use granular media permissions for photos/videos/audio

These changes make Android storage safer while maintaining file operation capabilities.