分区布局

在 Android 10 中,根文件系统已不再包含在 ramdisk.img 中,而是合并到了 system.img(即在创建 system.img 时始终将 BOARD_BUILD_SYSTEM_ROOT_IMAGE 视为已设置)。搭载 Android 10 的设备:

  • 使用 system-as-root 分区布局(由 build 自动执行,且不可选择更改这种行为)。
  • 必须使用 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,这会强制 build 将根文件系统合并到 system.img 中,然后将 system.img 作为根文件系统 (rootfs) 进行装载。此配置对于搭载 Android 9 的设备是强制性的,但对于升级到 Android 9 及搭载较低 Android 版本的设备是可选的。在 Android 10 system-as-root 配置中,build 始终将 $TARGET_SYSTEM_OUT$TARGET_ROOT_OUT 合并到 system.img 中;此配置是搭载 Android 10 的所有设备的默认行为。

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

以下部分将介绍系统专用 (system-only) OTA 的 system-as-root 要求,并提供有关将设备更新为使用 system-as-root 的指导(包括分区布局更改和 dm-verity 内核要求)。如需详细了解对 ramdisk 的更改,请参阅 Ramdisk 分区

关于系统专用 OTA

系统专用 OTA 需要 system-as-root 分区布局,这可以让 Android 版本在不更改其他分区的情况下更新 system.imgproduct.img。搭载 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(无缝)系统更新

使用供应商叠加层

使用供应商叠加层,您可以在设备启动时叠加对 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。此外,必须从通用内核 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 build,有用于 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 分区更新失败时,恢复模式可能无法正常工作。因此,在非 A/B 设备上,/recovery 分区必须/boot 分区分开,这意味着将继续延迟更新恢复映像(即和搭载 Android 8.1.0 或更低版本的设备一样)。

下表列出了非 A/B 设备在使用 Android 9 前后的映像分区差异。

映像 Ramdisk(Android 9 之前) System-as-root(Android 9 之后)
boot.img 包含内核与 ramdisk.img

ramdisk.img
  -/
    - init.rc
    - init
    - etc -> /system/etc
    - system/ (mount point)
    - vendor/ (mount point)
    - odm/ (mount point)
    ...
仅包含正常启动内核。
recovery.img 包含恢复内核与恢复 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-verity/(装载点)下装载 system.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,然后 external/avb/libavb 会解析 /system哈希树描述符,再将解析结果转换为 dm-verity 参数,最后通过内核命令行将这些参数传递给内核(/system 的哈希树描述符可能位于 /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) 之后(以及在运行供应商测试套件测试之前),任何通过 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/* 下自动创建这些装载点)。