Traditional storage

Android external storage HAL icon

Android supports devices with traditional storage, which is defined to be a case-insensitive filesystem with immutable POSIX permission classes and modes. The notion of traditional storage encompasses emulated and portable storage. Portable storage is defined as any external storage that is not adopted by the system and therefore not formatted and encrypted or tied to a specific device. Because traditional external storage offers minimal protection for stored data, system code should not store sensitive data on external storage. Specifically, configuration and log files should only be stored on internal storage where they can be effectively protected.

Multi-user external storage

Starting in Android 4.2, devices can support multiple users, and external storage must meet the following constraints:

  • Each user must have their own isolated primary external storage, and must not have access to the primary external storage of other users.
  • The /sdcard path must resolve to the correct user-specific primary external storage based on the user a process is running as.
  • Storage for large OBB files in the Android/obb directory may be shared between multiple users as an optimization.
  • Secondary external storage must not be writable by apps, except in package-specific directories as allowed by synthesized permissions.

The default platform implementation of this feature leverages Linux kernel namespaces to create isolated mount tables for each Zygote-forked process, and then uses bind mounts to offer the correct user-specific primary external storage into that private namespace.

At boot, the system mounts a single emulated external storage FUSE daemon at EMULATED_STORAGE_SOURCE, which is hidden from apps. After the Zygote forks, it bind mounts the appropriate user-specific subdirectory from under the FUSE daemon to EMULATED_STORAGE_TARGET so that external storage paths resolve correctly for the app. Because an app lacks accessible mount points for other users' storage, they can only access storage for the user it was started as.

This implementation also uses the shared subtree kernel feature to propagate mount events from the default root namespace into app namespaces, which ensures that features like ASEC containers and OBB mounting continue working correctly. It does this by mounting the rootfs as shared, and then remounting it as slave after each Zygote namespace is created.

Multiple external storage devices

Starting in Android 4.4, multiple external storage devices are surfaced to developers through Context.getExternalFilesDirs(), Context.getExternalCacheDirs(), and Context.getObbDirs().

External storage devices surfaced through these APIs must be a semi-permanent part of the device (such as an SD card slot in a battery compartment). Developers expect data stored in these locations to be available over long periods of time. For this reason, transient storage devices (such as USB mass storage drives) should not be surfaced through these APIs.

The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions. Restricting writes in this way ensures the system can clean up files when applications are uninstalled.

USB media support

Android 6.0 supports portable storage devices which are only connected to the device for a short period of time, like USB flash drives. When a user inserts a new portable device, the platform shows a notification to let them copy or manage the contents of that device.

In Android 6.0, any device that is not adopted is considered portable. Because portable storage is connected for only a short time, the platform avoids heavy operations such as media scanning. Third-party apps must go through the Storage Access Framework to interact with files on portable storage; direct access is explicitly blocked for privacy and security reasons.