Android Q privacy change: App-scoped and media-scoped storage

As of Android Q Beta 1, this change has the following properties:

  • Affects your app if you access and share files in external storage
  • Mitigate by using isolated sandbox or media collection directories
  • Enable behavior by running several ADB commands

To give users more control over their files and to limit file clutter, Android Q changes how apps can access files on the device's external storage. Android Q replaces the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions with more fine-grained, media-specific permissions, and apps accessing their own files on an external storage device don't require specific permissions. These changes affect how your app saves and accesses files on external storage.

This guide describes how to update your app so that it can continue to share, access, and update files that are saved on an external storage device, provides compatibility considerations, and explains how to activate this behavior change.

Isolated storage sandbox for app-private files

Android Q gives each app an isolated storage sandbox into an external storage device, such as /sdcard. No other app can directly access your app's sandboxed files. Because files are private to your app, you no longer need any permissions to access and save your own files within external storage. This change makes it easier to maintain the privacy of users' files and helps reduce the number of permissions that your app needs.

The best place to store files on external storage is in the location returned by Context.getExternalFilesDir(), because this location behaves consistently across all Android versions. When using this method, pass in the media environment corresponding to the type of file that you want to create or open. For example, to access or save app-private images, call Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES).

Shared collections for media files

If your app creates files that belong to the user, and that the user expects to be retained when your app is uninstalled, then save them into one of the common media collections, also known as shared collections. Shared collections include: Photos & Videos, Music, and Downloads.

Permissions for viewing other apps' files

Your app doesn't need to request any permissions in order to create and modify its own files within these shared collections. If your app needs to create and modify files that other apps have created, however, it must first request the appropriate permission:

  • Access to other apps' files in the Photos & Videos shared collection requires the READ_MEDIA_IMAGES or READ_MEDIA_VIDEO permission, depending on the type of file that your app needs to access.

  • Access to other apps' files in the Music shared collection requires the READ_MEDIA_AUDIO permission.

Learn more about how to work with other apps' files.

Access shared collections

After requesting the necessary permissions, your app accesses these collections using the MediaStore API:

  • For the Photos & Videos shared collection, use MediaStore.Images or MediaStore.Video.
  • For the Music shared collection, use MediaStore.Audio.
  • For the Downloads shared collection, use MediaStore.Downloads.

Preserve your app's files in shared collections

By default, when the user uninstalls your app, Android Q cleans up the files that you saved into your sandbox. To preserve these files when your app is uninstalled, use the Storage Access Framework, or save files to a shared collection.

To preserve files in a shared collection, insert a new row into the relevant MediaStore collection, populating its columns in the following way:

  • At a minimum, provide values for the DISPLAY_NAME and MIME_TYPE columns.
  • Optionally, you can influence where files are placed on disk using the PRIMARY_DIRECTORY and SECONDARY_DIRECTORY columns.
  • Leave the DATA column undefined. That way, the platform has the flexibility to preserve the file outside your sandbox.

After the row is inserted, you can use APIs like ContentResolver.openFileDescriptor() to read or write data to the newly-created file.

If the user reinstalls your app later, however, your app doesn't have access to those files unless it performs one of the following:

  • Requests the appropriate permission for the collection.
  • Sends a request to the user from the Storage Access Framework.

This situation is similar to the case where an app tries to access another app's files.

Special considerations for photographs

Android Q adds several enhancements to give users better control on how their photographs are accessed on external storage.

Access location information in pictures

Some photographs contain location information in their Exif metadata, which allows users to view the place where a photograph was taken. Because this location information is sensitive, Android Q redacts the information by default. This restriction to location information is different from the one that applies to camera characteristics.

If your app needs access to a photograph's location information, complete the following steps:

  1. Add the new ACCESS_MEDIA_LOCATION permission to your app's manifest.
  2. From your MediaStore object, call setRequireOriginal(), passing in the URI of the photograph.

If your app is a camera app, it doesn't have direct access to the photographs saved in the Photos & Videos shared collection unless it's the device's default Photo Manager app. To direct users to a gallery app, use the ACTION_REVIEW intent.

Work with other apps' files

This section explains how your app can interact with other apps' files that are stored in shared collections.

Access files created by other apps

To access and read media files that other apps have saved to an external storage device, complete the following steps:

  1. Request the necessary permission, based on the shared collection that contains the file you want to access.
  2. Use a ContentResolver object to find and open the file.

Write to files created by other apps

By saving a file to a shared collection, your app becomes that file's owner. Ordinarily, your app can write to a file in a shared collection only if you're the file owner. However, if your app has the correct role assigned to it, you can also write to files that other apps own:

  • If your app is the user's default Photo Manager app, you can modify image files that other apps saved to the Photos & Videos shared collection.
  • If your app is the users' default Music app, you can modify audio files that other apps saved to the Music shared collection.

To modify media files that other apps originally saved to an external storage device, complete one of the following steps:

  • Check whether your app is the default Photo Manager app or the default Music app by following the steps in the Roles guide.
  • Use a ContentResolver object to find the file and modify it in-place. When performing the edit/modify operation, catch the RecoverableSecurityException so that you can request that the user grant you write access to that specific item.

Access specific files

In some use cases, your app might need to open or create files that it doesn't have permission to access:

  • In a photo-editing app, open a drawing.
  • In a business productivity app, save a text document to a location that the user chooses.

For these situations, use the Storage Access Framework, which allows the user to select a specific file to open, or choose a specific location to save a file.

Companion app file sharing

If you manage a suite of apps that require mutual access to each other's files, use content:// URIs, which we already recommended as a security best practice.

For more information, see the documentation on how to set up file sharing.

Compatibility mode for previously installed apps on upgrading devices

The restrictions on accessing files in external storage apply only to apps that target Android Q, or apps that are newly installed on a device running Android Q.

The system places your app's file access privileges into compatibility mode when each of the following conditions is true:

  • Your app targets Android 9 (API level 28) or lower.
  • Your app is installed on a device that upgrades from Android 9 to Android Q.

When your app is in compatibility mode, the following file access behavior applies:

  • Your app can access all files stored within the MediaStore collections, even files that your app hasn't created.
  • The user-facing Storage permission allows or denies your app access to external storage as a whole, rather than individual shared collections like Photos & Videos or Music.

This compatibility mode remains in effect until your app is first uninstalled.

Identify a specific external storage device

In Android 9 (API level 28) and lower, all files on all storage devices appear under the single "external" volume name. Android Q gives each external storage device a unique volume name. This naming system helps you efficiently organize and index content, and it gives you control over where new content is stored.

To uniquely identify a specific file within external storage, you must use both the volume name and the ID together. For example, a file on the primary storage device would be content://media/external/images/media/12, but the corresponding file on a secondary storage device called FA23-3E92 would be content://media/FA23-3E92/images/media/12.

You can access the files stored on a particular volume by passing this volume name into a specific media collection, such as MediaStore.Images.getContentUri().

Get the list of external storage devices

To get the list of names for all currently-available volumes, call MediaStore.getAllVolumeNames(), as shown in the following code snippet:

Kotlin

val volumeNames: Set<String> = MediaStore.getAllVolumeNames(context)

Java

Set<String> volumeNames = MediaStore.getAllVolumeNames(context);

Set up a virtual external storage device

On devices without removable external storage, use the following command to enable to a virtual disk for testing purposes:

adb shell sm set-virtual-disk true

Test the behavior change

To help you make your app compatible with this new behavior change, the platform has provided several ways for you to tweak several parameters associated with the change.

Toggle the behavior change

To enable this behavior change in Android Q Beta 1, execute the following command in a terminal window:

adb shell sm set-isolated-storage on

The device restarts after you run this command. If it doesn't, wait a minute and try to run the command again.

To confirm that the behavior change has taken effect, use the following command:

adb shell getprop sys.isolated_storage_snapshot

Test compatibility mode behavior

When testing your app, you can enable compatibility mode for external file storage access by running the following command in a terminal window:

adb shell cmd appops set your-package-name android:legacy_storage allow

To disable compatibility mode, uninstall and reinstall your app on Android Q, or run the following command in a terminal window:

adb shell cmd appops set your-package-name android:legacy_storage default

Browse external storage as a file manager

To gain broad access to directories within external storage, as file manager apps might do, use the ACTION_OPEN_DOCUMENT_TREE intent. For an example, see the android-DirectorySelection sample on GitHub.