分区存储

分区存储会限制应用访问外部存储空间。在 Android 11 或更高版本中,以 API 30 或更高版本为目标平台的应用必须使用分区存储。之前,在 Android 10 中,应用可以选择停用分区存储。

应用访问限制

分区存储的目标是保护应用和用户数据的隐私。这包括保护用户信息(例如照片元数据)、防止应用在未经明确许可的情况下修改或删除用户文件,以及保护下载到“下载”或其他文件夹的敏感用户文档。

使用分区存储的应用可具有以下访问权限级别(实际访问权限因实现而异)。

  • 对自己的文件拥有读取写入访问权限(没有权限限制)
  • 对其他应用的媒体文件拥有读取访问权限(需要具备 READ_EXTERNAL_STORAGE 权限)
  • 只有在用户直接同意的情况下,才允许对其他应用的媒体文件拥有写入访问权限(系统图库以及符合“所有文件访问权限”获取条件的应用除外)
  • 对其他应用的外部应用数据目录没有读取写入访问权限

将分区存储与 FUSE 搭配使用

Android 11 或更高版本支持用户空间中的文件系统 (FUSE),这使 MediaProvider 模块可以检查用户空间中的文件操作并根据允许、拒绝或隐去访问权限的政策限制对文件的访问。分区存储中使用 FUSE 的应用可获得分区存储的隐私功能以及通过直接文件路径访问文件的功能(让 File API 继续在应用中运行)。

Android 10 对 MediaProvider 执行的文件访问强制实施了分区存储规则,但对直接文件路径访问(例如,使用 File API 和 NDK API)则不实施此类规则,因为拦截内核调用所需的工作量较大。因此,分区存储中的应用无法使用直接文件路径访问文件。此限制影响了应用开发者的适应能力,因为需要大量的代码更改才能将 File API 访问重写为 MediaProvider API 访问。

FUSE 和 SDCardFS

Android 11 对 FUSE 的支持与 SDCardFS 的弃用无关,但它为以前使用 SDCardFS 的设备提供了媒体库替代项。不同的设备:

  • 搭载 Android 11 或更高版本并运行内核版本 5.4 或更高版本的设备无法使用 SDCardFS。
  • 升级到 Android 11 或更高版本的设备可以基于 SDCardFS 托管 FUSE,以拦截文件操作并实现隐私目标。

FUSE 性能微调

之前,Android 7 或更低版本中支持 FUSE,其中的外部存储空间作为 FUSE 装载。由于这种 FUSE 实现存在性能和死锁问题,Android 8 引入了 SDCardFS。Android 11 使用经过改进且经过更充分测试的 libfuse 实现重新引入了对 FUSE 的支持,可以通过微调来解决 Android 7 或更低版本中的性能问题。

FUSE 微调包括以下调整项:

  • Android/dataAndroid/obb 目录绕过 FUSE,以提升依赖这些目录的游戏应用的性能。
  • 进行优化(例如微调 FUSE 文件系统的预读率和脏比率),以确保出色的读取性能和流畅的媒体播放。
  • 使用 FUSE 回写缓存。
  • 通过缓存权限来减少系统服务器的 IPC。
  • 对具有“所有文件访问权限”的应用进行优化,以加快批量操作的执行速度。

上述调整项可以在 FUSE 设备和非 FUSE 设备之间实现同等的性能。例如,对使用 FUSE 且微调后的 Pixel 2 以及使用媒体库的 Pixel 2 进行测试之后,我们发现文件路径访问和媒体库之间具有同等的顺序读取性能(例如视频播放)。不过,使用 FUSE 时的顺序写入性能稍差,随机读写可能会慢上一倍。

性能测量结果可能因设备和具体用例而异。由于 MediaProvider API 能提供最为一致的性能,因此关注性能的应用开发者应该为其应用使用 MediaProvider API。

减轻与 FUSE 相关的性能影响

只有大量使用存储在外部共享存储设备上的文件的应用才会受到与 FUSE 相关的性能影响。FUSE 会绕过外部专属存储空间(包括 android/dataandroid/obb 目录),而内部存储空间(如 /data/data,许多应用在这里存储数据以确保其获得加密和保护)不会装载 FUSE。

  • 只需少量使用共享外部存储空间的应用通常会与有限的一些文件(通常少于 100 个文件)交互。这些应用受益于现有的对常用读写操作的优化,在 Android 11 中应该不会受到任何与 FUSE 相关的性能影响。

  • 大量使用共享外部存储空间的应用通常会执行批量文件操作,例如列出或移除包含 1000 个文件的目录,或者在文件系统中创建或删除包含 100 万个文件的目录。在 Android 11 中,批量文件操作可能会受到 FUSE 的影响,但如果此类应用符合 MANAGE_EXTERNAL_STORAGE 权限的获取条件,它们将从 2020 年 10 月更新中包含的性能优化项中获益。

为了避免 FUSE 性能开销,应用可以将数据存储在外部专属存储空间中,或使用 ContentProvider 类中的批量 API 来绕过 FUSE 并获取性能经过优化的路径。此外,面向 MediaProvider 系统组件的 2020 年 10 月更新还包括针对拥有 MANAGE_EXTERNAL_STORAGE 权限的文件管理器和类似应用(如备份/恢复软件和杀毒软件)的性能优化项。

隐私优势远超性能劣势

在已针对 FUSE 进行微调的设备上,大多数关键用户历程在 Android 10 和 Android 11 之间的性能同等出色。不过,在针对一组文件操作执行基准测试时,Android 11 的性能可能比 Android 10 要差。对于 Android 11 中性能较差的文件访问模式(例如,随机读取或写入),我们建议使用 MediaProvider API 为应用提供非 FUSE 访问模式,这是确保可获取一致的高性能的最佳方案。

MediaProvider 和 FUSE 更新

MediaProvider 系统组件的行为因 Android 版本而异。

  • 在 Android 10 及更低版本中,SDCardFS 是文件系统,而 MediaProvider 为文件(如图片、视频和音乐文件等)集合提供了一个接口。应用使用 File API 创建一个文件时,它可以要求 MediaProvider 扫描该文件并将其记录在数据库中。

  • 在 Android 11 或更高版本中,SDCardFS 已废弃,MediaProvider 成为了外部存储空间的文件系统处理程序(适用于 FUSE),可使外部存储空间上的文件系统和 MediaProvider 数据库保持一致。作为 FUSE 文件系统的用户空间处理程序,MediaProvider 可以拦截内核调用并确保文件操作的隐私安全。

在 Android 11 及更高版本中,MediaProvider 也是一个可在 Android 版本之外更新的模块化系统组件(一个 Mainline 模块)。这意味着,MediaProvider 中存在的性能、隐私或安全问题可以通过 Google Play 商店或合作伙伴提供的其他机制以无线下载方式提交和修复。FUSE 处理程序牵涉的各类资源均可更新,这使得更新可以修复 FUSE 性能降低问题和 bug。