How to Configure Scoped Storage, MediaStore APIs, and Document Provider Permissions on Android

In early iterations of the Android operating system, applications requesting the broad READ_EXTERNAL_STORAGE or WRITE_EXTERNAL_STORAGE runtime permissions gained unrestricted access to the entire shared disk partition (/sdcard). This permissive model allowed rogue applications, adware trackers, and malicious APKs to harvest user documents, inspect sensitive database caches created by other apps, and leave behind persistent unmanaged debris upon uninstallation. Google overhauled this architecture with Scoped Storage.

Mandatory for modern Android targets, Scoped Storage re-architects shared storage access around privacy-preserving APIs: app-specific sandboxed directories, the indexed MediaStore API for shared media collections (images, video, audio), and the Storage Access Framework (SAF) for explicit user-directed document access. Understanding these permission boundaries and audit requirements for the privileged MANAGE_EXTERNAL_STORAGE exception is critical for mobile developers and enterprise device administrators.

Architectural Overview: Android Storage Partitions and Scoped Access

Android divides local physical flash memory into distinct security zones governed by Linux user IDs (UIDs) and SEAndroid policies:

Storage Zone Filesystem Location Required Permissions Persistence Lifecycle
App-Specific Internal Storage /data/data/<package>/ None (Private to app UID). Encrypted with CE/DE keys; wiped automatically on app uninstall.
App-Specific External Storage /sdcard/Android/data/<package>/ None (Scoped to package). Accessible without runtime permissions; wiped automatically on uninstall.
Shared MediaStore Collections MediaStore.Images, Audio, Video READ_MEDIA_IMAGES, READ_MEDIA_VIDEO (Android 13+). Persists indefinitely; files contributed by the app can be modified without permission.
Storage Access Framework (SAF) User-selected directories (ACTION_OPEN_DOCUMENT) Explicit system picker intent. Grants persistent scoped URI access tokens managed via ContentResolver.
All Files Access (Broad Exception) Global shared storage root (/sdcard/) MANAGE_EXTERNAL_STORAGE. Restricted Google Play policy exception (file managers, backup tools, antivirus).

Interacting with Shared Media via MediaStore APIs

Under Scoped Storage, applications cannot use standard POSIX file paths (e.g. /sdcard/DCIM/photo.jpg) directly. Media items must be queried, inserted, and updated via the ContentResolver and MediaStore API contracts.

Step 1: Inserting an Image into the Shared MediaStore

To save a rendered image into the shared public gallery without requesting legacy storage permissions:

// Define image metadata content values
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "audit_inspection_2026.jpg");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + "/EnterpriseAudits");
values.put(MediaStore.Images.Media.IS_PENDING, 1); // Locks item while writing

// Insert record into MediaStore
Uri collection = MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY);
Uri itemUri = context.getContentResolver().insert(collection, values);

// Open output stream and write byte array
try (OutputStream out = context.getContentResolver().openOutputStream(itemUri)) {
    bitmap.compress(Bitmap.CompressFormat.JPEG, 95, out);
}

// Clear pending flag to make image visible across other apps
values.clear();
values.put(MediaStore.Images.Media.IS_PENDING, 0);
context.getContentResolver().update(itemUri, values, null, null);

Step 2: Querying Shared Media Collections

Querying user photos requires requesting granular media permissions (READ_MEDIA_IMAGES on Android 13+):

String[] projection = new String[] {
    MediaStore.Images.Media._ID,
    MediaStore.Images.Media.DISPLAY_NAME,
    MediaStore.Images.Media.SIZE
};

try (Cursor cursor = context.getContentResolver().query(
        MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
        projection,
        null,
        null,
        MediaStore.Images.Media.DATE_ADDED + " DESC")) {
    int idColumn = cursor.getColumnIndexOrThrow(MediaStore.Images.Media._ID);
    while (cursor.moveToNext()) {
        long id = cursor.getLong(idColumn);
        Uri contentUri = ContentUris.withAppendedId(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, id);
        // Process image URI...
    }
}

Accessing Arbitrary Files via the Storage Access Framework (SAF)

For productivity applications that handle custom document formats (such as .pdf, .csv, or .zip), the Storage Access Framework (SAF) triggers the system-managed document picker, ensuring the user retains absolute control over file sharing.

Step 1: Launch the System Document Creation Picker

Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("application/pdf");
intent.putExtra(Intent.EXTRA_TITLE, "Quarterly_Financial_Report.pdf");
activity.startActivityForResult(intent, REQUEST_CODE_CREATE_DOC);

Step 2: Retain Persistent Scoped URI Access Tokens

When the user selects a directory via ACTION_OPEN_DOCUMENT_TREE, persist the permission grant across device restarts:

// Persist read/write access grant across device reboots
Uri treeUri = data.getData();
final int takeFlags = intent.getFlags() & (Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
context.getContentResolver().takePersistableUriPermission(treeUri, takeFlags);

Auditing and Enforcing MANAGE_EXTERNAL_STORAGE Permissions

Applications that genuinely require unrestricted filesystem access—such as dedicated file managers, antivirus scanners, and offline backup clients—must declare the high-privilege MANAGE_EXTERNAL_STORAGE permission.

Step 1: Manifest Declaration

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
</manifest>

Step 2: Checking State and Directing Users to System Settings

This permission cannot be granted via standard runtime prompt dialogs; users must be directed to a dedicated system settings page:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
    if (!Environment.isExternalStorageManager()) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
        intent.setData(Uri.parse("package:" + context.getPackageName()));
        context.startActivity(intent);
    }
}

Compliance Note: Google Play policy enforces strict verification before allowing apps with MANAGE_EXTERNAL_STORAGE into the store. Apps that can function with MediaStore or SAF will be rejected during review.

Troubleshooting Android Scoped Storage Exceptions

Consult this diagnostic reference table to resolve common runtime storage crashes and permission denials:

Exception / Error Underlying Root Cause Remediation Procedure
FileNotFoundException: open failed: EACCES (Permission denied) Attempted to access files outside app-specific directories using raw POSIX paths (new File("/sdcard/...")). Migrate file operations to the MediaStore API or ContentResolver.openInputStream(uri). Never assume direct filesystem path access on Android 11+.
RecoverableSecurityException thrown when editing media. App attempted to modify or delete a media file created by a different application. Catch RecoverableSecurityException, extract getUserAction().getActionIntent().getIntentSender(), and prompt the user to approve modification rights via startIntentSenderForResult().
SecurityException: Persistable URI permission limit exceeded The application exceeded the Android system limit of 128 persistent URI permissions per app. Audit active grants via getContentResolver().getPersistedUriPermissions() and release unused tokens using releasePersistableUriPermission().
requestLegacyExternalStorage ignored on Android 11+. android:requestLegacyExternalStorage="true" only applies when running on Android 10 (API 29); it is disabled on Android 11 (API 30+). Update codebase to fully support Scoped Storage; legacy storage flags provide no relief on modern Android releases.

Mastering Scoped Storage, MediaStore content resolvers, and Storage Access Framework tokens ensures robust, privacy-compliant data handling that satisfies modern Android security guidelines while maintaining seamless file interoperability.

Get the best tech tips delivered straight to your inbox.

Join thousands of readers mastering Apple, Google, Microsoft, and Linux.