传统存储

Android external storage HAL icon

Android 支持采用传统存储的设备,传统存储被定义为不区分大小写的文件系统,具有不可变的 POSIX 权限类和模式。传统存储的概念包括模拟存储和便携式存储。便携式存储定义为系统未采纳的任何外部存储,因此未格式化和加密,也未绑定到特定设备。由于传统外部存储为存储的数据提供的保护最少,因此系统代码不应在外部存储上存储敏感数据。具体而言,配置和日志文件应仅存储在内部存储中,以便可以对其进行有效保护。

多用户外部存储

从 Android 4.2 开始,设备可以支持多用户,并且外部存储必须满足以下约束条件

  • 每个用户都必须有自己隔离的主外部存储,并且不得访问其他用户的主外部存储。
  • /sdcard 路径必须根据进程运行的用户解析为正确的用户专用主外部存储。
  • 为了进行优化,Android/obb 目录中用于大型 OBB 文件的存储空间可以在多个用户之间共享。
  • 辅助外部存储不得由应用写入,除非在合成权限允许的软件包专用目录中。

此功能的默认平台实现利用 Linux 内核命名空间为每个 Zygote 分叉的进程创建隔离的挂载表,然后使用绑定挂载将正确的用户专用主外部存储提供到该私有命名空间中。

在启动时,系统会在 EMULATED_STORAGE_SOURCE 挂载一个模拟的外部存储 FUSE 守护进程,该守护进程对应用是隐藏的。在 Zygote 分叉后,它会将 FUSE 守护进程下相应的用户特定子目录绑定挂载到 EMULATED_STORAGE_TARGET,以便外部存储路径能够为应用正确解析。由于应用缺少对其他用户存储的访问挂载点,因此它们只能访问以启动用户身份的存储。

此实现还使用共享子树内核功能将挂载事件从默认根命名空间传播到应用命名空间,这确保了 ASEC 容器和 OBB 挂载等功能继续正常工作。它通过将 rootfs 挂载为共享来实现这一点,然后在创建每个 Zygote 命名空间后将其重新挂载为从属。

多个外部存储设备

从 Android 4.4 开始,多个外部存储设备通过 Context.getExternalFilesDirs()Context.getExternalCacheDirs()Context.getObbDirs() 向开发者公开。

通过这些 API 公开的外部存储设备必须是设备的半永久性组成部分(例如电池仓中的 SD 卡插槽)。开发者期望存储在这些位置的数据在很长一段时间内都可用。因此,不应通过这些 API 公开瞬态存储设备(例如 USB 大容量存储驱动器)。

WRITE_EXTERNAL_STORAGE 权限必须仅授予对设备上主外部存储的写入访问权限。除非在合成权限允许的包特定目录中,否则不得允许应用写入辅助外部存储设备。以这种方式限制写入可确保系统在卸载应用程序时能够清理文件。

USB 媒体支持

Android 6.0 支持便携式存储设备,这些设备仅在短时间内连接到设备,例如 USB 闪存驱动器。当用户插入新的便携式设备时,平台会显示通知,让他们复制或管理该设备的内容。

在 Android 6.0 中,任何未被采纳的设备都被视为便携式设备。由于便携式存储仅连接很短时间,因此平台避免了媒体扫描等繁重操作。第三方应用必须通过 Storage Access Framework 与便携式存储上的文件进行交互;出于隐私和安全原因,直接访问被明确阻止。