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.