传统存储设备

Android 外部存储设备 HAL 图标

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 卡插槽)。开发者希望存储在这些位置的数据可供长期使用。因此,瞬态存储设备(如 USB 大容量存储驱动器)不应通过这些 API 提供。

WRITE_EXTERNAL_STORAGE 权限必须仅向在设备上的主要外部存储设备授予写入权限。不允许应用写入次要外部存储设备,除非是在应用的软件包专属目录中(因为有合成权限)。以这种方式限制写入可确保系统在应用被卸载时将文件清理干净。

USB 媒体支持

Android 6.0 支持只需短时间内连接到设备的便携式存储设备,如 U 盘。当用户插入新的便携式设备时,该平台会显示一条通知,以让用户复制或管理相应设备上的内容。

在 Android 6.0 中,任何未合并的设备均被视为便携式设备。由于便携式存储设备只能短时间连接到设备,因此 Android 平台会避免执行媒体扫描之类的繁重操作。第三方应用必须通过存储访问框架与便携式存储设备中的文件进行交互;出于隐私和安全考虑,明确禁止直接访问。