分区布局

在 Android 10 中,根文件系统不再包含在 ramdisk.img 中,而是合并到 system.img 中(即,system.img 的创建方式始终如同设置了 BOARD_BUILD_SYSTEM_ROOT_IMAGE)。搭载 Android 10 的设备

  • 使用 system-as-root 分区布局(通过构建自动强制执行,没有用于更改行为的选项)。
  • 必须使用 ramdisk,这是 dm-linear 所必需的。
  • 必须将 BOARD_BUILD_SYSTEM_ROOT_IMAGE 设置为 false。此设置仅用于区分使用 ramdisk 的设备和不使用 ramdisk(而是直接挂载 system.img)的设备。

system-as-root 配置的含义在 Android 9 和 Android 10 之间有所不同。在 Android 9 system-as-root 配置中,BOARD_BUILD_SYSTEM_ROOT_IMAGE 设置为 true,这会强制构建将根文件系统合并到 system.img 中,然后将 system.img 挂载为根文件系统 (rootfs)。对于搭载 Android 9 的设备,此配置是强制性的,但对于升级到 Android 9 的设备以及运行较低版本 Android 的设备,此配置是可选的。在 Android 10 system-as-root 配置中,构建始终将 $TARGET_SYSTEM_OUT$TARGET_ROOT_OUT 合并到 system.img 中;此配置是所有运行 Android 10 的设备的默认行为。

Android 10 进一步更改以支持 动态分区,这是一种用户空间分区系统,支持通过无线 (OTA) 更新来创建、调整大小或销毁分区。作为此更改的一部分,Linux 内核不再能够在运行 Android 10 的设备上挂载逻辑系统分区,因此此操作由第一阶段 init 处理。

以下部分介绍仅系统 OTA 的 system-as-root 要求,并提供有关更新设备以使用 system-as-root 的指南(包括分区布局更改和 dm-verity 内核要求)。有关 ramdisk 更改的详细信息,请参阅 Ramdisk 分区

关于仅系统 OTA

仅系统 OTA 允许 Android 版本更新 system.imgproduct.img,而无需更改其他分区,这需要 system-as-root 分区布局。所有运行 Android 10 的设备都必须使用 system-as-root 分区布局才能启用仅系统 OTA。

  • A/B 设备将 system 分区挂载为 rootfs,已经使用 system-as-root,并且不需要更改即可支持系统 OTA。
  • 非 A/B 设备将 system 分区挂载在 /system,必须更新为使用 system-as-root 分区布局才能支持系统 OTA。

有关 A/B 和非 A/B 设备的详细信息,请参阅 A/B(无缝)系统更新

使用供应商叠加层 (<=AOSP 14)

供应商叠加层允许您在设备启动时将更改叠加到 vendor 分区。供应商叠加层是 product 分区中的一组供应商模块,这些模块在设备启动时叠加在 vendor 分区上,替换和添加到现有模块。

当设备启动时,init 进程完成第一阶段挂载并读取默认属性。然后,它搜索 /product/vendor_overlay/<target_vendor_version> 并在满足以下条件时将其每个子目录挂载到其对应的 vendor 分区目录:

  • /vendor/<overlay_dir> 存在。
  • /product/vendor_overlay/<target_vendor_version>/<overlay_dir> 具有与 /vendor/<overlay_dir> 相同的文件上下文。
  • init 允许挂载到 /vendor/<overlay_dir> 的文件上下文。

实现供应商叠加层

将供应商叠加层文件安装在 /product/vendor_overlay/<target_vendor_version> 中。这些文件在设备启动时叠加 vendor 分区,替换同名文件并添加任何新文件。供应商叠加层无法从 vendor 分区中删除文件。

供应商叠加层文件必须具有与它们在 vendor 分区中替换的目标文件相同的文件上下文。默认情况下,/product/vendor_overlay/<target_vendor_version> 目录中的文件具有 vendor_file 上下文。如果供应商叠加层文件与它们替换的文件之间存在文件上下文不匹配,请在设备特定的 sepolicy 中指定。文件上下文是在目录级别设置的。如果供应商叠加层目录的文件上下文与目标目录不匹配,并且未在设备特定的 sepolicy 中指定正确的文件上下文,则该供应商叠加层目录不会叠加到目标目录上。

要使用供应商叠加层,内核必须通过设置 CONFIG_OVERLAY_FS=y 来启用 OverlayFS。此外,内核必须从 common kernel 4.4 或更高版本合并,或者使用 "overlayfs: override_creds=off option bypass creator_cred" 进行修补。

供应商叠加层实现示例

此过程演示了如何实现一个供应商叠加层,该叠加层叠加 /vendor/lib/*/vendor/etc/*/vendor/app/* 目录。

  1. device/<vendor>/<target>/vendor_overlay/<target_vendor_version>/ 中添加预构建的供应商文件

    device/google/device/vendor_overlay/28/lib/libfoo.so
    device/google/device/vendor_overlay/28/lib/libbar.so
    device/google/device/vendor_overlay/28/etc/baz.xml
    device/google/device/vendor_overlay/28/app/qux.apk
  2. device/google/device/device.mk 中将预构建的供应商文件安装到 product/vendor_overlay

    PRODUCT_COPY_FILES += \
        $(call find-copy-subdir-files,*,device/google/device/vendor_overlay,$(TARGET_COPY_OUT_PRODUCT)/vendor_overlay)
  3. 如果目标 vendor 分区文件具有 vendor_file 以外的上下文,则定义文件上下文。由于 /vendor/lib/* 使用 vendor_file 上下文,因此本示例不包含该目录。

    将以下内容添加到 device/google/device-sepolicy/private/file_contexts

    /(product|system/product)/vendor_overlay/[0-9]+/etc(/.*)?   u:object_r:vendor_configs_file:s0
    /(product|system/product)/vendor_overlay/[0-9]+/app(/.*)?   u:object_r:vendor_app_file:s0
  4. 允许 init 进程在 vendor_file 以外的文件上下文中挂载供应商叠加层。由于 init 进程已经具有在 vendor_file 上下文中挂载的权限,因此本示例未定义 vendor_file 的策略。

    将以下内容添加到 device/google/device-sepolicy/public/init.te

    allow init vendor_configs_file:dir mounton;
    allow init vendor_app_file:dir mounton;

验证供应商叠加层

要验证供应商叠加层配置,请在 /product/vendor_overlay/<target_vendor_version>/<overlay_dir> 中添加文件,并检查这些文件是否叠加在 /vendor/<overlay_dir> 中的文件上。

对于 userdebug 版本,有一个用于 Atest 的测试模块

$ atest -v fs_mgr_vendor_overlay_test

更新到 system-as-root

要更新非 A/B 设备以使用 system-as-root,您必须更新 boot.imgsystem.img 的分区方案,设置 dm-verity,并删除对设备特定根文件夹的任何启动依赖项。

更新分区

与将 /boot 重新用作 recovery 分区的 A/B 设备不同,非 A/B 设备必须保持 /recovery 分区独立,因为它们没有回退槽分区(例如,从 boot_aboot_b)。如果在非 A/B 设备上删除 /recovery 并使其类似于 A/B 方案,则在 /boot 分区更新失败期间,recovery 模式可能会中断。因此,对于非 A/B 设备,/recovery 分区必须是独立于 /boot 的分区,这意味着 recovery 镜像继续以延迟方式更新(即,与运行 Android 8.1.0 或更低版本的设备相同)。

下表列出了 Android 9 之前和之后非 A/B 设备的镜像分区差异。

镜像 Ramdisk(版本 9 之前) System-as-root(版本 9 之后)
boot.img 包含内核和 ramdisk.img
ramdisk.img
  -/
    - init.rc
    - init
    - etc -> /system/etc
    - system/ (mount point)
    - vendor/ (mount point)
    - odm/ (mount point)
    ...
仅包含正常的启动内核。
recovery.img 包含 recovery 内核和 recovery ramdisk.img
system.img 包含以下内容
system.img
  -/
    - bin/
    - etc
    - vendor -> /vendor
    - ...
包含原始 system.imgramdisk.img 的合并内容
system.img
  -/
    - init.rc
    - init
    - etc -> /system/etc
    - system/
      - bin/
      - etc/
      - vendor -> /vendor
      - ...
    - vendor/ (mount point)
    - odm/ (mount point)
    ...

分区本身没有变化;ramdisk 和 system-as-root 都使用以下分区方案

  • /boot
  • /system
  • /system
  • /recovery
  • /vendor 等。

设置 dm-verity

在 system-as-root 中,内核必须使用 dm-veritysystem.img 挂载在 /(挂载点)下。AOSP 支持以下 system.img 的 dm-verity 实现。

vboot 1.0

对于 vboot 1.0,内核必须解析 /system 上的 Android 特定元数据,然后转换为 dm-verity 参数以设置 dm-verity(需要这些内核补丁)。以下示例显示了内核命令行中 system-as-root 的 dm-verity 相关设置

ro root=/dev/dm-0 rootwait skip_initramfs init=/init
dm="system none ro,0 1 android-verity /dev/sda34"
veritykeyid=id:7e4333f9bba00adfe0ede979e28ed1920492b40f

vboot 2.0

对于 vboot 2.0 (AVB),引导加载程序必须集成 external/avb/libavb,然后它会解析 hashtree 描述符 /system,将其转换为 dm-verity 参数,最后通过内核命令行将参数传递给内核。(/system 的 Hashtree 描述符可能位于 /vbmeta/system 本身。)

vboot 2.0 需要以下内核补丁

以下示例显示了内核命令行中 system-as-root 的 dm-verity 相关设置

ro root=/dev/dm-0 rootwait  skip_initramfs init=/init

dm="1 vroot none ro 1,0 5159992 verity 1
PARTUUID=00000016-0000-0000-0000-000000000000
PARTUUID=00000016-0000-0000-0000-000000000000 4096 4096 644999 644999
sha1 d80b4a8be3b58a8ab86fad1b498640892d4843a2
8d08feed2f55c418fb63447fec0d32b1b107e42c 10 restart_on_corruption
ignore_zero_blocks use_fec_from_device
PARTUUID=00000016-0000-0000-0000-000000000000 fec_roots 2 fec_blocks
650080 fec_start 650080"

使用设备特定的根文件夹

使用 system-as-root 后,在设备上刷入通用系统映像 (GSI) 之后(以及在运行 Vendor Test Suite 测试之前),使用 BOARD_ROOT_EXTRA_FOLDERS 添加的任何设备特定根文件夹都将消失,因为整个根目录内容已被 system-as-root GSI 替换。如果存在对设备特定根文件夹的依赖项(例如,它们用作挂载点),则删除这些文件夹可能会导致设备无法启动。

为避免此问题,请不要使用 BOARD_ROOT_EXTRA_FOLDERS 添加设备特定的根文件夹。如果需要指定设备特定的挂载点,请使用 /mnt/vendor/<mount point>(在这些更改列表中添加)。这些供应商特定的挂载点可以直接在 fstab 设备树(用于第一阶段挂载)和 /vendor/etc/fstab.{ro.hardware} 文件中指定,而无需额外设置(因为 fs_mgr 会自动在 /mnt/vendor/* 下创建它们)。