Scoped Storage is a file system access model introduced in Android 10 (API 29) that restricts arbitrary app access to the device's shared storage. According to Google Android Developer Documentation (2024), Scoped Storage replaces the old permission model of READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE, granting apps access only to their own files in an isolated sandbox and to shared collections through MediaStore. This model enhances user data privacy and prevents unauthorized collection of information by apps.
Key Takeaways
Scoped Storage is an architectural change in Android 10 (API 29) that fundamentally changes how apps access the device's file system. Instead of full access to shared storage, the app gets access only to its own sandbox and to specific media collections through MediaStore.
Before Scoped Storage, any app with READ_EXTERNAL_STORAGE permission could read all files in shared storage — photos, documents, downloads, data from other apps. This created privacy risks: untrustworthy apps could collect user information without their knowledge.
According to Google I/O 2019, over 65% of Android users consider data privacy a critical factor when choosing an app. Scoped Storage is a direct response to this demand: each app operates in an isolated environment and gains access to other apps' data only with explicit user consent.
Important: Scoped Storage in Android 10 was optional (apps could opt out via requestLegacyExternalStorage). Starting from Android 11 (API 30), Scoped Storage became mandatory for all apps, regardless of target SDK.
Google introduced Scoped Storage to solve three fundamental problems of the old file access model: privacy, permission management, and residual file cleanup.
In the old model, an app with READ_EXTERNAL_STORAGE could scan the entire shared storage and collect user file metadata — photo geotags, document names, directory structures. Scoped Storage eliminates this possibility: even with permission, the app only sees files it created itself and files explicitly selected by the user through SAF.
Before Scoped Storage, apps could leave files in shared storage after uninstallation. Over time, junk directories accumulated. Scoped Storage solves this: all files in the app's sandbox are removed with the app, and files through MediaStore have an owner and can be cleaned by the system.
| Aspect | Old model (Legacy) | Scoped Storage |
|---|---|---|
| Access to shared storage | Full (with permission) | Only own sandbox |
| Media files | Direct file path | Via MediaStore URI |
| Other apps' files | Always accessible | Only through SAF |
| Cleanup on uninstall | Files remain | Sandbox is removed |
| User control | Minimal | Explicit consent per file |
Scoped Storage is part of Google's overall privacy strategy in Android, which also includes "only while using" permissions, camera and microphone access indicators, and the Privacy Dashboard.
Each app in Android receives its own private directory, accessible via Context.getFilesDir() and context.getCacheDir(). For access to the external (shared) private directory, Context.getExternalFilesDir() is used.
In Scoped Storage, an app has full access to its external private directory without any permissions. This is the primary place for storing files that should not be accessible to other apps or that the app creates for its own use.
import android.os.Environment
import java.io.File
import java.io.IOException
class FileManager {
fun saveToAppStorage(context: Context, fileName: String, data: ByteArray) {
val appDir = context.getExternalFilesDir(null)
?: return
val file = File(appDir, fileName)
file.writeBytes(data)
}
fun readFromAppStorage(context: Context, fileName: String): ByteArray? {
val appDir = context.getExternalFilesDir(null)
?: return null
val file = File(appDir, fileName)
return file.takeIf { it.exists() }?.readBytes()
}
}
The path to the external private directory: /storage/emulated/0/Android/data/{packageName}/files/. Starting from Android 11, direct file path is not available — only through the API. This is another security enhancement of Scoped Storage.
MediaStore is the primary API for accessing shared media files (images, videos, audio) in Scoped Storage. The app works not with file paths but with content URIs provided by the MediaStore provider.
MediaStore is divided into three main collections: Images, Video, and Audio. Each collection supports CRUD operations via ContentResolver. To insert a new file into the Images collection, MediaStore.Images.Media is used; to query existing ones — query with the corresponding URI.
import android.content.ContentValues
import android.provider.MediaStore
import android.os.Environment
import java.io.OutputStream
fun saveImageToGallery(context: Context, bitmap: Bitmap, title: String) {
val values = ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, "$title.jpg")
put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg")
put(
MediaStore.Images.Media.RELATIVE_PATH,
"${Environment.DIRECTORY_PICTURES}/MyApp"
)
}
val uri = context.contentResolver
.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values)
?: return
context.contentResolver.openOutputStream(uri)?.use { output: OutputStream ->
bitmap.compress(
Bitmap.CompressFormat.JPEG,
95,
output
)
}
}
Important: MediaStore does not support arbitrary file paths — only content URIs. Attempting to get a file path from a URI via MediaStore.Files.getContentUri() will not yield a direct file path on Android 11+. Instead, use ContentResolver.openInputStream() and openOutputStream() to work with content.
Storage Access Framework (SAF) is an API for accessing arbitrary files and directories outside the app's sandbox. SAF provides the user with a file or directory picker interface, after which the app receives a content URI with temporary access.
SAF is used for scenarios not covered by MediaStore: working with arbitrary documents (PDF, ZIP, APK), accessing directories on the SD card, importing and exporting files from other apps. The user explicitly selects a file through the system file picker — this guarantees their consent for access.
import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.provider.DocumentsContract
const val REQUEST_CODE_PICK_DIR = 1001
fun pickDirectory(activity: Activity) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
activity.startActivityForResult(intent, REQUEST_CODE_PICK_DIR)
}
fun handlePickResult(requestCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE_PICK_DIR && data != null) {
val treeUri: Uri = data.data ?: return
// Take persistent URI permission
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or
Intent.FLAG_GRANT_WRITE_URI_PERMISSION
contentResolver.takePersistableUriPermission(treeUri, flags)
}
}
SAF gives the app access to the user-selected directory until device reboot (when using takePersistableUriPermission). This is the only way to gain access to arbitrary files in shared storage on Android 11+.
Migrating an existing Android app to Scoped Storage requires changes in several key areas. Google recommends a phased approach with testing on Android 11+.
Replace all direct File operations in shared storage with ContentResolver.openInputStream / openOutputStream. For files in the app's sandbox (getExternalFilesDir), file paths continue to work.
Remove READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE if they are not needed for specific scenarios (e.g., file management — for this there is a special permission MANAGE_EXTERNAL_STORAGE).
| Scenario | Old approach | Scoped Storage approach |
|---|---|---|
| Saving a photo | File(path).writeBytes() | MediaStore + ContentResolver |
| Reading PDF | File(path).inputStream() | SAF ACTION_OPEN_DOCUMENT |
| Own files | Environment.getExternalStorageDirectory() | context.getExternalFilesDir() |
| Cache | File(cacheDir).writeBytes() | context.cacheDir (no changes) |
After migration, test the app on Android 11+ (API 30) by setting targetSdk = 30 or higher in build.gradle. Make sure all file operations in shared storage work through MediaStore or SAF, not through direct file paths.
import android.os.Build
fun isScopedStorage(): Boolean {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
}
fun compatReadFile(context: Context, uri: Uri): ByteArray? {
return if (isScopedStorage()) {
context.contentResolver
.openInputStream(uri)?.readBytes()
} else {
File(uri.path ?: return null)
.takeIf { it.exists() }
?.readBytes()
}
}
According to Google Play Console (2024), over 78% of active Android devices run Android 10+ with Scoped Storage. Migration is a mandatory step for publishing updates on Google Play: new apps must target API 31+, updates — API 30+.
Frequently Asked Questions
No, starting from Android 11 (API 30) Scoped Storage is mandatory for all apps. The requestLegacyExternalStorage flag, available in Android 10, does not work on API 30+. The only way to get broad file system access is the MANAGE_EXTERNAL_STORAGE permission, but it is intended only for file managers and antivirus apps.
MANAGE_EXTERNAL_STORAGE is a special permission for apps that need full file system access (file managers, backup tools). Requesting this permission opens a system screen with a warning for the user. When publishing on Google Play, you must fill out a Declaration form about the necessity of this permission.
In Scoped Storage on Android 11+, you cannot get a direct file path from a content URI. Instead, use ContentResolver.openInputStream() for reading and openOutputStream() for writing. If you need a file path for compatibility with a third-party library, create a copy of the file in getCacheDir() and work with the copy.
FileProvider continues to work without changes — it is used to provide access to files from the app's sandbox to other apps via content URIs. Scoped Storage does not affect FileProvider, since FileProvider works at the content URIs level, not direct file paths.
Check Build.VERSION.SDK_INT: if >= Build.VERSION_CODES.Q (29), then the device supports Scoped Storage. However, on Android 10, Scoped Storage may be disabled via requestLegacyExternalStorage. On Android 11+, check if the requestLegacyExternalStorage manifest flag is set — if not, Scoped Storage is active.
Summary
We will develop a mobile application turnkey
IT Sectr creates iOS and Android applications for startups and businesses since 2017. We will advise you and propose the best solution.
Read also