
Android supports devices with external storage, which is defined to be a case-insensitive filesystem with immutable POSIX permission classes and modes. External storage can be provided by physical media (such as an SD card), or by exposing a portion of internal storage through an emulation layer. Devices may contain multiple instances of external storage.
Access to external storage is protected by various Android
permissions. Starting in Android 1.0, write access is protected with the
WRITE_EXTERNAL_STORAGE
permission. Starting in Android 4.1,
read access is protected with the READ_EXTERNAL_STORAGE
permission.
Starting in Android 4.4, the owner, group and modes of files on external
storage devices are now synthesized based on directory structure. This
enables apps to manage their package-specific directories on external
storage without requiring they hold the broad
WRITE_EXTERNAL_STORAGE
permission. For example, the app with
package name com.example.foo
can now freely access
Android/data/com.example.foo/
on external storage devices with
no permissions. These synthesized permissions are accomplished by wrapping
raw storage devices in a FUSE daemon.
Since 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.
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()
.
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.
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.