Android: Scoped Storage

Mohit Dubey
5 min readFeb 27, 2024

--

Scoped storage is a mechanism introduced by Google in Android 10 (API level 29) to enhance user privacy and security by restricting access to a broader range of storage areas on the device. It changes how apps store and access files on external storage, such as SD cards, while providing users with more control over their data.

Before scoped storage, apps had broad access to the entire external storage (Shared Storage), including the user’s media files, downloads, and other app-specific directories. However, this unrestricted access raised privacy concerns and made it easier for apps to access sensitive user data.

With scoped storage, each app is given its isolated storage area on external storage, restricting direct access to files outside of this designated area. Apps can still access shared media files using new APIs provided by Android.

Here’s how scoped storage works:

  1. Private App-Specific Directory: Each app has its isolated storage space on external storage. This space is private to the app, and other apps cannot access files stored within it directly.
  2. Access to Shared Collections: While direct access to shared collections like the user’s photos, videos, and audio files is restricted, Android provides MediaStore and other APIs for apps to access these collections indirectly. Apps must use these APIs to interact with shared media files.
  3. App-Specific Permissions: Apps need specific permissions to access files outside their isolated storage. For example, to access photos from the user’s gallery, apps must request the READ_EXTERNAL_STORAGE permission and use the appropriate APIs.
  4. Scoped Access to Specific Files: Apps can still request access to specific files outside their isolated storage using the Storage Access Framework (SAF). This allows users to grant access to specific files when needed, ensuring better control over their data.

Scoped storage improves user privacy and security by limiting apps’ access to sensitive data while still allowing them to function effectively. However, it requires developers to update their apps to comply with the new storage model and use the provided APIs for accessing shared collections and requesting access to specific files.

Here’s a brief example of how an app might interact with scoped storage:

import android.Manifest
import android.content.ContentResolver
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Bundle
import android.provider.MediaStore
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

class MainActivity : AppCompatActivity() {

private val REQUEST_READ_EXTERNAL_STORAGE = 1

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

readFromGallery()
}

private fun readFromGallery() {
// Check if the app has permission to read from external storage
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE)
== PackageManager.PERMISSION_GRANTED) {
// Use MediaStore to access images
val uri: Uri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI
val projection = arrayOf(MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME)
val selection: String? = null
val selectionArgs: Array<String>? = null
val sortOrder: String? = null

val resolver: ContentResolver = contentResolver
val cursor = resolver.query(uri, projection, selection, selectionArgs, sortOrder)

cursor?.use {
while (cursor.moveToNext()) {
// Process each image
val imageName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DISPLAY_NAME))
// Do something with the image name
}
}
} else {
// Request permission from the user
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE),
REQUEST_READ_EXTERNAL_STORAGE)
}
}
}

Scoped Storage is a paradigm shift in how Android apps interact with storage resources on the device.

Here are some key aspects and features of Scoped Storage:

  1. Isolated App-Specific Storage:
  • Each app is allocated its isolated storage space on external storage. This space is private to the app, and other apps cannot access files stored within it directly. This ensures better data security and prevents apps from accessing sensitive user data belonging to other apps.

2. Restricted Access to Shared Collections:

  • Direct access to shared media collections, such as photos, videos, and audio files, is restricted. Apps can no longer freely access these files from arbitrary locations on the external storage. Instead, they must use specific APIs provided by Android to interact with shared media files indirectly.

3. MediaStore API:

  • Android provides the MediaStore API, which allows apps to access shared media files like images, videos, and audio files. Apps can query the MediaStore to retrieve metadata and content URIs for media files, enabling them to work with shared media content in a controlled manner.

4. Scoped Access via Storage Access Framework (SAF):

  • Apps can request access to specific files outside their isolated storage using the Storage Access Framework. When an app needs access to files outside its isolated storage, it can prompt the user to select files using the SAF, thereby granting scoped access to those files. This ensures that apps only access files explicitly authorized by the user.

5. Enhanced User Privacy and Security:

  • Scoped Storage enhances user privacy and security by limiting apps’ access to sensitive data. By isolating app-specific storage and restricting access to shared collections, Scoped Storage reduces the risk of unauthorized access to user data and mitigates potential security vulnerabilities.

6. Transition Period and Compatibility:

  • With the introduction of Scoped Storage, there has been a transition period during which developers were encouraged to update their apps to comply with the new storage model. Google provided guidelines and resources to help developers migrate their apps to Scoped Storage while ensuring compatibility with older Android versions.

Overall, Scoped Storage represents a significant improvement in how Android apps manage storage resources and handle user data. It aligns with Google’s broader efforts to prioritize user privacy and security in the Android ecosystem while providing developers with the tools and resources they need to adapt their apps to the new storage model.

BONUS:

Storage Access Framework (SAF):

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {

private val REQUEST_PICK_FILE = 100

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

// Launch SAF intent when a button is clicked
// For demonstration purposes, you can trigger this on any UI event
buttonSelectFile.setOnClickListener {
openFilePicker()
}
}

private fun openFilePicker() {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "*/*" // Set MIME type to filter files, e.g., "image/*" for images
startActivityForResult(intent, REQUEST_PICK_FILE)
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)

if (requestCode == REQUEST_PICK_FILE && resultCode == Activity.RESULT_OK) {
data?.data?.let { uri ->
// Handle the selected file URI
Toast.makeText(this, "Selected file: $uri", Toast.LENGTH_SHORT).show()
// You can use the URI to read or manipulate the selected file
// For example, you can open an InputStream to read the file's contents
}
}
}
}
  • We define a constant REQUEST_PICK_FILE to identify the request when receiving the result.
  • In the openFilePicker() function, we create an intent with the action Intent.ACTION_OPEN_DOCUMENT to prompt the user to select a file.
  • We add the category Intent.CATEGORY_OPENABLE to filter the intent to show only files that can be opened (i.e., documents).
  • We set the MIME type to */* to indicate that any type of file can be selected. You can specify specific MIME types (e.g., "image/" for images, "audio/" for audio files).
  • We start the activity for result using startActivityForResult() and pass the intent along with the request code REQUEST_PICK_FILE.
  • In the onActivityResult() method, we handle the result of the file picker activity. If the result is successful (resultCode == Activity.RESULT_OK), we retrieve the selected file's URI from the returned intent's data.

This example demonstrates a basic implementation of using SAF to allow users to select files from external storage. Depending on your app’s requirements, you may need to perform additional tasks such as requesting runtime permissions, handling permission results, and processing the selected file’s content.

Thanks For Reading.

--

--

Mohit Dubey

Experienced Android Developer with 3 years of Java and Kotlin expertise. Specialized in designing, developing, and deploying high-quality Android applications.