Android 分区

分区概览

下面列出标准Android系统中存在的分区,供应商可能对分区进行增减和更改。需要注意的是,Android 9开始对分区结构增加新的技术(A/B 设备、system-as-root等),它们对分区作用有很大影响。

  • boot:包含通过 mkbootimg 组合在一起的kernel镜像和 ramdisk。使能system-as-root后,该分区仅存放kernel镜像。
  • system:主要包含 Android 框架。使能system-as-root后,该分区会包含原始 system.img 和 ramdisk.img 的合并内容。
  • recovery:用于存储在 OTA 升级时使用的recovery系统。如果设备支持A/B更新,OTA升级可以通过ramdisk执行,该分区可以不需要。
  • cache:用于存储临时数据,OTA升级包也会下载到这个分区。如果设备使用 A/B 更新,则可以不要此分区。
  • misc:分区供recovery使用,存储空间不能小于 4KB。
  • userdata:存储用户安装的应用和数据。
  • metadata:如果设备被加密,则需要使用该分区,分区的存储空间不能小于 16MB。
  • vendor:包含所有不可分发给 Android 开源项目 (AOSP) 的二进制文件。如果没有专有信息,可以不要该分区。
  • radio:包含无线装置映像。只有包含无线装置的设备才需要此分区。
  • tos:用于存储 Trusty 操作系统的二进制映像文件,仅在设备包含 Trusty 时使用。
  • product:用于存放产品专用的配置和应用,以便OEM定制自己的系统。Android 9 及更高版本支持该分区。product分区是对system分区的扩展,必须同时升级这两个分区。
  • odm:用于ODM自定义自己的板级支持包。Android 10 开始支持该分区。odm分区是对vendor分区的扩展,必须同时升级这两个分区。

Product分区

许多 OEM 会自定义 AOSP 系统映像,以实现自己的功能并满足运营商的要求。不过,如果进行这类自定义,则无法针对多个软件 SKU 使用单个系统映像。映像必须各不相同,才能支持不同的语言区域、运营商等自定义。如果使用单独的 /product 分区来包含自定义项,则可以针对多个软件 SKU 使用单个系统映像。

/product 分区包含以下组件:

  • 产品专用的系统属性 (/product/build.prop)
  • 产品专用的 RRO (/product/overlay/*.apk)
  • 产品专用的应用 (/product/app/*.apk)
  • 产品专用的特权应用 (/product/priv-app/*.apk)
  • 产品专用的内容库 (/product/lib/*)
  • 产品专用的 Java 库 (/product/framework/*.jar)
  • 产品专用的 Android 框架系统配置(/product/etc/sysconfig/*/product/etc/permissions/*
  • 产品专用的媒体文件 (/product/media/audio/*)
  • 产品专用的 bootanimation 文件

Android 9 中的 /product 分区是 /system 分区的扩展。由于 /product/system 分区之间的 ABI 稳定性较弱,因此必须同时升级这两者,而且 ABI 应基于系统 SDK。如果系统 SDK 不涵盖 /product/system 之间的所有 API 表面,则 OEM 必须在这两个分区之间维护自己的 ABI。

/product 分区和 /system 分区可以相互依赖。不过,在没有 /product 分区的情况下,对通用系统映像 (GSI) 的测试必须能够正常运行。/product 分区不能对 /vendor 分区有任何依赖。/product/vendor 分区之间不允许有任何直接交互。(这一规则将由 SEpolicy 强制执行。)

为了实现新的Product分区,请添加以下的编译标记:

  • BOARD_USES_PRODUCTIMAGE
  • BOARD_PRODUCTIMAGE_PARTITION_SIZE
  • BOARD_PRODUCTIMAGE_FILE_SYSTEM_TYPE
  • PRODUCT_PRODUCT_PROPERTIES for /product/build.prop。这些必须在 $(call inherit-product path/to/device.mk) 内,例如 PRODUCT_PRODUCT_PROPERTIES += product.abc=ok

product 分区中安装模块时,请添加以下编译标记:

  • Android.bp 中的 product_specific: true
  • Android.mk 中的 LOCAL_PRODUCT_MODULE := true

为防止 /product 分区被恶意软件篡改,您应该为该分区启用 Android 启动时验证 (AVB)(就像为 /vendor/ 和 /system 分区启用一样)。要启用 AVB,请添加以下编译标记:

  • BOARD_AVB_PRODUCT_ADD_HASHTREE_FOOTER_ARGS

ODM 分区

原始设计制造商 (ODM)可能需要实现自己的内核模块,或 替换或自定义 SoC 组件。 这就需要自定义SoC 供应商的板级支持包 (BSP)。之前 Android 版本,自定义BSP需要更改vendor映像,这就会阻止相同 SoC设备使用同一vendor映像。在 Android 10 中,系统构建了单独的 /odm 分区,自定义行为存放在/odm分区中,因而能够针对多个硬件 SKU 使用单个供应商映像。

/odm 分区包含以下 ODM 专用组件(类似于 /vendor 分区),如下表所示。

ODM 专用组件 位置
可加载内核模块 (LKM) /odm/lib/modules/*.ko
原生库 /odm/lib[64]
HAL /odm/lib[64]/hw
SEPolicy /odm/etc/selinux
VINTF 对象数据 /odm/etc/vintf
init.rc 文件 /odm/etc/init
系统属性 /odm/build.prop
运行时资源叠加层 (RRO) /odm/overlay/*.apk
应用 /odm/app/*.apk
特权应用 /odm/priv-app/*.apk
Java 库 /odm/framework/*.jar
Android 框架系统配置 /odm/etc/sysconfig/*/odm/etc/permissions/*

odm 分区是 /vendor 分区的扩展。在考虑应用二进制接口 (ABI) 稳定性时,请记住以下架构:

build_sys_to_prod_odm_partitions.png

  • /odm/vendor 分区之间不具有 ABI 稳定性。必须同时升级这两个分区。
  • /odm/vendor 分区可以相互依赖,但是在没有 /odm 分区的情况下,/vendor 分区必须运行。
  • /odm/system 之间的 ABI 与 /vendor/system 之间的 ABI 相同。

/product 分区与 /vendor/odm 分区之间不允许有任何直接交互。(这一规则将由 SEpolicy 强制执行。)

要设置 /odm 分区,请添加以下构建标记:

  • BOARD_ODMIMAGE_PARTITION_SIZE(适用于固定分区大小)
  • PRODUCT_USE_DYNAMIC_PARTITIONSBOARD_ODMIMAGE_PARTITION_RESERVED_SIZE(适用于动态分区大小)
  • BOARD_ODMIMAGE_FILE_SYSTEM_TYPE 文件系统类型(用于 ODM 映像)
  • PRODUCT_ODM_PROPERTIES(适用于 /odm/build.prop
    $(call inherit-product path/to/device.mk) 中使用该标记,例如 PRODUCT_ODM_PROPERTIES += product.abc=ok

使用以下构建标记向 /odm 分区中安装模块:

  • Android.bp 中的 device_specific: true
  • Android.mk 中的 LOCAL_ODM_MODULE := true

要防止恶意软件篡改 /odm 分区,请为这些分区启用 Android 启动时验证 (AVB)(就像为 /vendor/system 分区启用一样)。要启用 AVB,请添加构建标记 BOARD_AVB_ODM_ADD_HASHTREE_FOOTER_ARGS

/product 分区的支持,让您可以通过使用不同 product.img 映像来提供的多个软件 SKU,并保证它们使用单个system映像。/odm 分区让你可以在多个硬件SKU中使用同一vendor映像。这样,整个系统变得很有层次。使用 /system 分区来托管通用代码(这类代码在许多软件 SKU 之间共享),使用 /vendor 分区来托管 SoC 专属 BSP 代码(这类代码基于指定 SoC 在多台设备之间共享)。

分区布局的变化

system-as-root

system-as-root的含义是使用system分区做为根目录。Android 9默认使用 system-as-root,但Android 10在启动是依然会使用ramdisk,之后会将system分区切换为root目录。

  • Android 9BOARD_BUILD_SYSTEM_ROOT_IMAGE 设为 true时,会强制编译将根文件系统合并到 system.img 中,然后将 system.img 作为根文件系统 (rootfs) 进行装载。此配置对于搭载 Android 9 的设备是强制性的,但对于升级到 Android 9 及搭载较低 Android 版本的设备是可选的。
  • Android 10:编译始终将 $TARGET_SYSTEM_OUT$TARGET_ROOT_OUT 合并到 system.img 中;此配置是搭载 Android 10 的所有设备的默认行为。boot.img在第一阶段依然做为根文件系统加载,之后执行/initsystem.img加载到/system上。然后执行切换根操作将装载从 /system 移动到 /,装载完成后ramdisk 的内容将会释放。

要将非 A/B 设备在 Android 9 上更新为使用 system-as-root,您必须更新 boot.imgsystem.img 的分区架构、设置 dm-verity,并移除特定于设备的根文件夹中的任何启动依赖项。

  • 更新分区boot.img中将原有的ramdisk.img去除,仅包含正常启动内核。 system.img 中增加ramdisk.img的内容,就是说rootfs会合并到 system.img 中。
  • 设置 dm-verity:在 system-as-root 中,内核必须使用 dm-verity 在 /(装载点)下装载 system.img。AOSP 支持 vboot 1.0vboot 2.0 的dm-verity 实现。
  • 不使用设备特定的根文件夹:使用 system-as-root 时,在设备上刷写常规系统映像 (GSI) 之后(以及在运行供应商测试套件测试之前),任何通过 BOARD_ROOT_EXTRA_FOLDERS 添加的特定于设备的根文件夹都会消失,因为整个根目录内容已被 system-as-root GSI 取代。为避免此问题,请勿使用 BOARD_ROOT_EXTRA_FOLDERS 来添加特定于设备的根文件夹。如果需要指定设备特定的装载点,请使用 /mnt/vendor/<mount point>

A/B 设备

Android 9开始,系统支持A/B设备。因此可以在分区上将设备分为两种,

  • A/B设备:系统在磁盘空间上划分为两个Slot:Slot A和Slot B。需要独立升级的分区(boot、recovery、system、radio、vendor等)在每个Slot上都会存在一个分区。例如boot会存在boot_a和boot_b两个分区,一个做为当前工作分区,另一个做为备份分区。OTA升级时,会在备份的Slot上更新,更新成功后将当前工作Slot进行切换,类似一种乒乓工作模式。
  • 非A/B设备:之前Android系统的分区方式。OTA升级时在recovery模式下通过cache分区进行更新。存在的问题是不够灵活且无法保证安全。

对于非A/B设备,分区布局在各Android版本间的布局也是不同的。Android 9中使用system-as-root,没有了ramdisk。Android 10使用类似 ramdisk + system-as-root 的方式,ramdisk中包含第一阶段 Init 和 fstab 。第一阶段 Init 是位于 /init 的静态可执行文件,Init会将 system.img 作为 /system 进行装载,然后执行切换根操作将装载从 /system 移动到 /。装载完成后,ramdisk 的内容将会释放。

下表总结了非A/B设备在各Android版本上的分区布局。

partition_noAB.png

对于A/B设备,在Android 9之前时不支持的,Android 9和Android 10间的主要区别是boot分区中ramdisk的差别。Android 10上的第一阶段Init使用了recovery ramdisk 中的 /init。设备首先将根切换到 /first_stage_ramdisk,以便从环境中移除recovery组件,然后装载system.img 和释放ramdisk。如果内核命令行中存在 androidboot.force_normal_boot=1,则设备会正常启动(启动到 Android)而不是启动到恢复模式。

下表总结了A/B设备在各Android版本上的分区布局。

partition_AB.png

动态分区

Android 10支持了动态分区,这是一种可以通过无线下载 (OTA) 更新来创建、销毁分区或调整分区大小的用户空间分区系统。系统为设备分配一个 super 分区,其中的子分区可动态地调整大小。单个分区映像不再需要为将来的 OTA 预留空间,super 中剩余的可用空间可用于所有动态分区。

动态分区是使用 Linux 内核中的 dm-linear device-mapper 模块实现的。super 分区中包含列出 super 内每个动态分区的名称和块范围的元数据。在第一阶段 init 执行期间,系统会解析和验证这些元数据,并创建虚拟块设备来表示每个动态分区。

应用 OTA 时,系统会根据需要自动创建/删除动态分区,或者调整动态分区的大小。若是 A/B 设备,将存在两个元数据副本,而更改仅会应用到表示目标槽位的副本。 由于动态分区是在用户空间中实现的,因此引导加载程序所需的分区不能是动态的。例如,引导加载程序会读取 bootdtbovbmeta,因此这些分区必须仍保持为物理分区。

在新设备上实现动态分区

分区更改

对于搭载 Android 10 的设备,请创建名为 super 的分区。super 分区在内部处理 A/B 槽位,因此 A/B 设备不需要单独的 super_asuper_b 分区。引导加载程序未使用的所有只读 AOSP 分区都必须是动态的,并且必须从 GUID 分区表 (GPT) 中移除。供应商专用分区则可以不是动态的,并且可以放在 GPT 中。

支持的动态分区包括:system、vendor、product、system、odm。下图显示转换为动态分区前后变化,

dynamic_partitions_table.png

分区对齐

如果 super 分区未正确对齐,device-mapper 模块的运行效率可能会降低。super 分区必须与最小 I/O 请求大小保持一致,该大小由块层决定。默认情况下,编译系统(通过生成 super 分区映像的 lpmake)认为对每个动态分区应用 1 MiB 的对齐就已经足够。不过,供应商应确保 super 分区正确对齐。

您可以通过检查 sysfs 来确定块设备的最小请求大小。例如:

# ls -l /dev/block/by-name/super
lrwxrwxrwx 1 root root 16 1970-04-05 01:41 /dev/block/by-name/super -> /dev/block/sda17
# cat /sys/block/sda/queue/minimum_io_size
786432

您可以使用类似的方法验证 super 分区的对齐:

# cat /sys/block/sda/sda17/alignment_offset

对齐偏移必须为 0。

设备配置更改

要支持动态分区,请在 device.mk 中添加以下标记:

PRODUCT_USE_DYNAMIC_PARTITIONS := true

板级配置更改

您需要设置 super 分区的大小:

BOARD_SUPER_PARTITION_SIZE := <size-in-bytes>

在 A/B 设备上,如果动态分区映像的总大小超过 super 分区大小的一半,编译系统就会发生错误。

您可以按以下方式配置动态分区列表。对于使用更新组的设备,请在 BOARD_SUPER_PARTITION_GROUPS 变量中列出这些组。然后,每个组名都会有 BOARD_group_SIZEBOARD_group_PARTITION_LIST 变量。对于 A/B 设备,组的大小上限应仅包含一个槽位,因为组名在内部以槽位为后缀。

下面的设备示例将所有分区放入名为 example_dynamic_partitions 的组中:

BOARD_SUPER_PARTITION_GROUPS := example_dynamic_partitions
BOARD_EXAMPLE_DYNAMIC_PARTITIONS_SIZE := 6442450944
BOARD_EXAMPLE_DYNAMIC_PARTITIONS_PARTITION_LIST := system vendor product

下面的设备示例将系统和产品服务放入 group_foo,并将 vendorproductodm 放入 group_bar

BOARD_SUPER_PARTITION_GROUPS := group_foo group_bar
BOARD_GROUP_FOO_SIZE := 4831838208
BOARD_GROUP_FOO_PARTITION_LIST := system product_services
BOARD_GROUP_BAR_SIZE := 1610612736
BOARD_GROUP_BAR_PARTITION_LIST := vendor product odm  
  • 对于 A/B 启动设备,所有组的大小上限总和必须为:
    BOARD_SUPER_PARTITION_SIZE / 2 - 开销
  • 对于非 A/B 设备和改造的 A/B 设备,所有组的大小上限总和必须为:
    BOARD_SUPER_PARTITION_SIZE - 开销
  • 在编译时,更新组中每个分区的映像大小总和不得超过组的大小上限。
  • 在计算时需要扣除开销,因为要考虑元数据、对齐等。合理的开销是 4 MiB,但您可以根据设备的需要选择更大的开销。

调整动态分区的大小

在采用动态分区之前,会为分区分配富余的空间,以确保它们有足够的空间满足将来的更新。使用动态分区后,你但可以在 OTA 期间增加分区容量。确保分区在合理使用的条件下,分区尽可能减少可用空间。对于只读的 ext4 映像,如果未指定分区大小,则编译系统会自动分配最小的空间。编译系统会适配映像,以尽可能减少文件系统中的未使用空间。这样可以确保设备不会浪费可用于 OTA 的空间。

此外,通过启用块级重复信息删除,可以进一步压缩 ext4 映像。要启用此功能,请使用以下配置:

BOARD_EXT4_SHARE_DUP_BLOCKS := true

如果不希望自动分配最小分区大小,则可以通过两种方法来控制分区大小。您可以使用 BOARD_partitionIMAGE_PARTITION_RESERVED_SIZE 指定最小可用空间,也可以指定 BOARD_partitionIMAGE_PARTITION_SIZE,强制将动态分区设为特定大小。除非必要,这两种方法都不建议使用。

例如:

BOARD_PRODUCTIMAGE_PARTITION_RESERVED_SIZE := 52428800

这会强制 product.img 中的文件系统保留 50 MiB 的未使用空间。

System-as-root 更改

具有动态分区的设备(无论是搭载动态分区还是改造动态分区)不得使用纯粹 system-as-root。因为Linux 内核无法解读 super 分区,也就无法装载 systemsystem 需要 ramdisk 中的第一阶段 init 装载。

请勿设置 BOARD_BUILD_SYSTEM_ROOT_IMAGE。在 Android 10 中,BOARD_BUILD_SYSTEM_ROOT_IMAGE 标记仅用于区分系统是由内核装载,还是由 ramdisk 中的第一阶段 init 装载。如果将 BOARD_BUILD_SYSTEM_ROOT_IMAGE 设置为 true,则当 PRODUCT_USES_DYNAMIC_PARTITIONS 也为 true 时,就会导致编译错误。

BOARD_USES_RECOVERY_AS_BOOT 设置为 true 时,recovery映像将被编译为 boot.img,其中包含recovery的 ramdisk。以前,引导加载程序使用 skip_initramfs 内核命令行参数来决定启动到哪种模式。而对于 Android 10 设备,引导加载程序不能向内核命令行传递 skip_initramfs,而应传递 androidboot.force_normal_boot=1 来跳过recovery并正常启动 Android。

AVB 配置更改

使用 Android 启动时验证 2.0 时,如果设备未使用链式分区描述符,则不需要进行更改。但如果使用了链式分区,并且其中一个已验证分区是动态分区,则需要进行更改。

下面的设备配置示例链接 systemvendor 分区所对应的 vbmeta

BOARD_AVB_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
BOARD_AVB_SYSTEM_ALGORITHM := SHA256_RSA2048
BOARD_AVB_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
BOARD_AVB_SYSTEM_ROLLBACK_INDEX_LOCATION := 1

BOARD_AVB_VENDOR_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
BOARD_AVB_VENDOR_ALGORITHM := SHA256_RSA2048
BOARD_AVB_VENDOR_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
BOARD_AVB_VENDOR_ROLLBACK_INDEX_LOCATION := 1

使用该配置,引导加载程序可以在 systemvendor 分区的末尾找到 vbmeta 页脚。由于这两个分区对引导加载程序不再可见(它们位于 super 中),因此需要进行两项更改。

  • 在设备的分区表中添加 vbmeta_systemvbmeta_vendor 分区。若是 A/B 设备,请添加 vbmeta_system_avbmeta_system_bvbmeta_vendor_avbmeta_vendor_b。如果添加上述一个或多个分区,则它们的大小应与 vbmeta 分区相同。
  • 通过添加 VBMETA_来重命名配置标记,并指定链接扩展到的分区:

    BOARD_AVB_VBMETA_SYSTEM := system
    BOARD_AVB_VBMETA_SYSTEM_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
    BOARD_AVB_VBMETA_SYSTEM_ALGORITHM := SHA256_RSA2048
    BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
    BOARD_AVB_VBMETA_SYSTEM_ROLLBACK_INDEX_LOCATION := 1
    
    BOARD_AVB_VBMETA_VENDOR := vendor
    BOARD_AVB_VBMETA_VENDOR_KEY_PATH := external/avb/test/data/testkey_rsa2048.pem
    BOARD_AVB_VBMETA_VENDOR_ALGORITHM := SHA256_RSA2048
    BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX := $(PLATFORM_SECURITY_PATCH_TIMESTAMP)
    BOARD_AVB_VBMETA_VENDOR_ROLLBACK_INDEX_LOCATION := 1

一个设备可能会使用其中的一个或两个分区,也可能一个也不使用。只有在链接到逻辑分区时才需要进行更改。

AVB 引导加载程序更改

如果引导加载程序已嵌入 libavb,请包含以下补丁程序:

如果使用链式分区,请包含一个额外的补丁程序:

  • 49936b4c0109411fdd38bd4ba3a32a01c40439a9 -“libavb:支持在分区开头存放 vbmeta blob。”

    注意:以前,在 AvbOps 中实现 get_size_of_partition 是可选的。在此更改之后,则变成了必需的,并且引导加载程序也必须实现此函数。

内核命令行更改

必须在内核命令行中添加新参数 androidboot.boot_devicesinit 使用它来启用 /dev/block/by-name 符号链接。该参数应该是由 ueventd 创建的底层 by-name 符号链接(即 /dev/block/platform/device-path/by-name/partition-name)中的设备路径部分。

例如,如果 super 分区的 by-name 符号链接是 /dev/block/platform/soc/100000.ufshc/by-name/super,则您可以在 BoardConfig.mk 文件中按以下方式添加该命令行参数:

BOARD_KERNEL_CMDLINE += androidboot.boot_devices=soc/100000.ufshc

fstab 更改

设备树和设备树叠加层不得包含 fstab 条目。使用将成为 ramdisk 一部分的 fstab 文件。

必须对逻辑分区的 fstab 文件进行以下更改:

  • fs_mgr 标记字段中必须包含 logical 标记和 first_stage_mount 标记。first_stage_mount 标记在 Android 10 中引入,指示将在第一阶段装载分区。
  • 分区可以将 avb=vbmeta partition name 作为 fs_mgr 标记进行指定,随后该指定的 vbmeta 分区将先由第一阶段 init 初始化,然后再尝试装载设备。
  • dev 字段必须是分区名称。

以下 fstab 条目按照上述规则设置 system、vendor 和 product 逻辑分区。

#<dev>  <mnt_point> <type>  <mnt_flags options> <fs_mgr_flags>
system   /system     ext4    ro,barrier=1        wait,slotselect,avb=vbmeta,logical,first_stage_mount
vendor   /vendor     ext4    ro,barrier=1        wait,slotselect,avb,logical,first_stage_mount
product  /product    ext4    ro,barrier=1        wait,slotselect,avb,logical,first_stage_mount
注意:对于非 A/B 设备,请勿包含 slotselect

将 fstab 文件复制到第一阶段 ramdisk。

SELinux 更改

必须使用 super_block_device 标签标记 super 分区块设备。例如,如果 super 分区的 by-name 符号链接是 /dev/block/platform/**soc/100000.ufshc**/by-name/super,请将以下行添加到 file_contexts

/dev/block/platform/soc/10000\.ufshc/by-name/super   u:object_r:super_block_device:s0

fastbootd

引导加载程序(或任何非用户空间刷写工具)无法理解动态分区,因此无法对其进行刷写。为解决此问题,设备必须使用 fastboot 协议的用户空间实现,称为 fastbootd。

adb remount

对于使用 eng 或 userdebug 编译的开发者,adb remount 对于实现快速迭代非常有用。动态分区给 adb remount 造成了问题,因为每个文件系统中都不再有空闲空间。为解决此问题,设备可以启用 overlayfs。只要 super 分区中有空闲空间,adb remount 就会自动创建临时的动态分区,并使用 overlayfs 进行写入。该临时分区的名称为 scratch,因此请勿将此名称用于其他分区。

升级 Android 设备

如果您想将设备升级到 Android 10,并且希望在 OTA 中包含动态分区支持,则不需要更改内置分区表。需要进行一些额外的配置。

设备配置更改

要改造动态分区,请在 device.mk 中添加以下标记:

PRODUCT_USE_DYNAMIC_PARTITIONS := true
PRODUCT_RETROFIT_DYNAMIC_PARTITIONS := true

板级配置更改

您需要设置以下板级变量:

  • BOARD_SUPER_PARTITION_BLOCK_DEVICES 设置为用于存储动态分区区段的块设备的列表。这是设备上现有物理分区的名称列表。
  • 通过 BOARD_SUPER_PARTITION_partition_DEVICE_SIZE 分别设置 BOARD_SUPER_PARTITION_BLOCK_DEVICES 中的每个块设备的大小。这是设备上现有物理分区的大小列表。在现有的板级配置中,这通常为 BOARD_partitionIMAGE_PARTITION_SIZE
  • BOARD_SUPER_PARTITION_BLOCK_DEVICES 中的所有分区取消现有的 BOARD_partitionIMAGE_PARTITION_SIZE 设置。
  • BOARD_SUPER_PARTITION_SIZE 设置为 BOARD_SUPER_PARTITION_partition_DEVICE_SIZE 的总和。
  • BOARD_SUPER_PARTITION_METADATA_DEVICE 设置为存储动态分区元数据的块设备。它必须是 BOARD_SUPER_PARTITION_BLOCK_DEVICES 中的一个。通常,将其设置为 system
  • 分别设置 BOARD_SUPER_PARTITION_GROUPSBOARD_group_SIZEBOARD_group_PARTITION_LIST

例如,如果设备已经有 system 和 vendor 分区,并且您希望在更新期间将它们转换为动态分区并添加新的 product 分区,请设置以下板级配置:

BOARD_SUPER_PARTITION_BLOCK_DEVICES := system vendor
BOARD_SUPER_PARTITION_METADATA_DEVICE := system

# Rename BOARD_SYSTEMIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE.
BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE := <size-in-bytes>

# Rename BOARD_VENDORIMAGE_PARTITION_SIZE to BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE
BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE := <size-in-bytes>

# This is BOARD_SUPER_PARTITION_SYSTEM_DEVICE_SIZE + BOARD_SUPER_PARTITION_VENDOR_DEVICE_SIZE
BOARD_SUPER_PARTITION_SIZE := <size-in-bytes>

# Configuration for dynamic partitions. For example:
BOARD_SUPER_PARTITION_GROUPS := group_foo
BOARD_GROUP_FOO_SIZE := <size-in-bytes>
BOARD_GROUP_FOO_PARTITION_LIST := system vendor product

SELinux 更改

必须使用 super_block_device_type 属性标记 super 分区块设备。例如,如果设备已经有 systemvendor 分区,并且您希望将它们作为存储动态分区区段的块设备,则应将它们的 by-name 符号链接标记为 system_block_device

/dev/block/platform/soc/10000\.ufshc/by-name/system   u:object_r:system_block_device:s0
/dev/block/platform/soc/10000\.ufshc/by-name/vendor   u:object_r:system_block_device:s0

然后,将以下行添加到 device.te

typeattribute system_block_device super_block_device_type;

出厂映像

对于搭载动态分区支持的设备,请勿使用用户空间 fastboot 来刷写出厂映像,因为启动到用户空间比其他刷写方法慢。

为了解决此问题,make dist 现在会编译一个额外的 super.img 映像,该映像可以直接刷写到 super 分区。它会自动捆绑逻辑分区的内容,这意味着除了 super 分区元数据外,它还包含 system.imgvendor.img 等。此映像可以直接刷写到 super 分区,无需进行任何其他加工,也无需使用 fastbootd。编译之后,super.img 会存放在 ${ANDROID_PRODUCT_OUT} 中。

对于搭载动态分区的 A/B 设备,super.img 包含 A 槽位中的映像。直接刷写 super 映像后,在重启设备之前将槽位 A 标记为可启动。

对于改造设备,make dist 会编译一组可以直接刷写到相应物理分区的 super_*.img 映像。例如,当 BOARD_SUPER_PARTITION_BLOCK_DEVICES 是“system vendor”时,make dist 会编译 super_system.imgsuper_vendor.img。系统会将这些映像存放在 target_files.zip 的 OTA 文件夹中。

设备映射的存储设备调整

动态分区可以容纳许多不确定的设备映射对象。它们可能不会像预期的那样全部实例化,因此您必须跟踪所有mounts,并更新所有底层存储涉笔关联分区的Android属性。

Init中有一个机制可以跟踪mounts并异步更新Android属性。但它花费的时间不能保证在特定时间内,因此必须为所有on-property的触发提供足够的时间。这些属性为dev.mnt.blk.<partition>,其中<partition>rootsystemdatavendor。每个属性都与存储设备名称关联,如以下所示:

taimen:/ % getprop | grep dev.mnt.blk
[dev.mnt.blk.data]: [sda]
[dev.mnt.blk.firmware]: [sde]
[dev.mnt.blk.metadata]: [sde]
[dev.mnt.blk.persist]: [sda]
[dev.mnt.blk.root]: [dm-0]
[dev.mnt.blk.vendor]: [dm-1]

blueline:/ $ getprop | grep dev.mnt.blk
[dev.mnt.blk.data]: [dm-4]
[dev.mnt.blk.metadata]: [sda]
[dev.mnt.blk.mnt.scratch]: [sda]
[dev.mnt.blk.mnt.vendor.persist]: [sdf]
[dev.mnt.blk.product]: [dm-2]
[dev.mnt.blk.root]: [dm-0]
[dev.mnt.blk.system_ext]: [dm-3]
[dev.mnt.blk.vendor]: [dm-1]
[dev.mnt.blk.vendor.firmware_mnt]: [sda]

init.rc允许将Android属性扩展为规则的一部分,平台可以根据需要使用以下命令调整存储设备:

write /sys/block/${dev.mnt.blk.root}/queue/read_ahead_kb 128
write /sys/block/${dev.mnt.blk.data}/queue/read_ahead_kb 128

当第二阶段init中开始处理命令时,epoll loop将变为活动状态,并且属性值将开始更新。但是,由于属性触发直到late-init时才处于活动状态,因此在初始启动阶段不能使用它们来处理rootsystem、或vendor。可能在init.rc脚本中early-fs阶段(当各种守护进程和工具启动时)可以修改read-ahead-kb之前,内核默认的read-ahead-kb已经足够。因此,Google建议使用on-property特性和init.rc-controlled属性(如sys.read_ahead_kb)来处理时序操作和防止竞争,如以下示例所示:

on property:dev.mnt.blk.root=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.root}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.system=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.system}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.vendor=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.vendor}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.product=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.system_ext}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.oem=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.oem}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on property:dev.mnt.blk.data=* && property:sys.read_ahead_kb=*
    write /sys/block/${dev.mnt.blk.data}/queue/read_ahead_kb ${sys.read_ahead_kb:-2048}

on early-fs:
    setprop sys.read_ahead_kb ${ro.read_ahead_kb.boot:-2048}

on property:sys.boot_completed=1
   setprop sys.read_ahead_kb ${ro.read_ahead_kb.bootcomplete:-128}z`
参考文档:

Partition Layout
Partitions and Images

阅读 3.8k

推荐阅读
老王系统屋
用户专栏

做为一个不称职的老年码农,一直疏忽整理笔记,开博记录一下,用来丰富老年生活,

7 人关注
52 篇文章
专栏主页