分区概览
下面列出标准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) 稳定性时,请记住以下架构:
-
/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_PARTITIONS
和BOARD_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 9:
BOARD_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
在第一阶段依然做为根文件系统加载,之后执行/init
将system.img
加载到/system
上。然后执行切换根操作将装载从/system
移动到/
,装载完成后ramdisk 的内容将会释放。
要将非 A/B 设备在 Android 9 上更新为使用 system-as-root,您必须更新 boot.img
和 system.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.0
和vboot 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版本上的分区布局。
对于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版本上的分区布局。
动态分区
Android 10支持了动态分区,这是一种可以通过无线下载 (OTA) 更新来创建、销毁分区或调整分区大小的用户空间分区系统。系统为设备分配一个 super
分区,其中的子分区可动态地调整大小。单个分区映像不再需要为将来的 OTA 预留空间,super
中剩余的可用空间可用于所有动态分区。
动态分区是使用 Linux 内核中的 dm-linear device-mapper 模块实现的。super
分区中包含列出 super
内每个动态分区的名称和块范围的元数据。在第一阶段 init
执行期间,系统会解析和验证这些元数据,并创建虚拟块设备来表示每个动态分区。
应用 OTA 时,系统会根据需要自动创建/删除动态分区,或者调整动态分区的大小。若是 A/B 设备,将存在两个元数据副本,而更改仅会应用到表示目标槽位的副本。 由于动态分区是在用户空间中实现的,因此引导加载程序所需的分区不能是动态的。例如,引导加载程序会读取 boot
、dtbo
和 vbmeta
,因此这些分区必须仍保持为物理分区。
在新设备上实现动态分区
分区更改
对于搭载 Android 10 的设备,请创建名为 super
的分区。super
分区在内部处理 A/B 槽位,因此 A/B 设备不需要单独的 super_a
和 super_b
分区。引导加载程序未使用的所有只读 AOSP 分区都必须是动态的,并且必须从 GUID 分区表 (GPT) 中移除。供应商专用分区则可以不是动态的,并且可以放在 GPT 中。
支持的动态分区包括:system、vendor、product、system、odm。下图显示转换为动态分区前后变化,
分区对齐
如果 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_SIZE
和 BOARD_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
,并将 vendor
、product
和 odm
放入 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
分区,也就无法装载 system
。 system
需要 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 时,如果设备未使用链式分区描述符,则不需要进行更改。但如果使用了链式分区,并且其中一个已验证分区是动态分区,则需要进行更改。
下面的设备配置示例链接 system
和 vendor
分区所对应的 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
使用该配置,引导加载程序可以在 system
和 vendor
分区的末尾找到 vbmeta 页脚。由于这两个分区对引导加载程序不再可见(它们位于 super
中),因此需要进行两项更改。
- 在设备的分区表中添加
vbmeta_system
和vbmeta_vendor
分区。若是 A/B 设备,请添加vbmeta_system_a
、vbmeta_system_b
、vbmeta_vendor_a
和vbmeta_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,请包含以下补丁程序:
- 818cf56740775446285466eda984acedd4baeac0 -“libavb:仅在 cmdline 需要时才查询分区 GUID。”
- 5abd6bc2578968d24406d834471adfd995a0c2e9 -”允许不存在 system 分区”
- 9ba3b6613b4e5130fa01a11d984c6b5f0eb3af05 -“修复 AvbSlotVerifyData->cmdline 可能为 NULL”
如果使用链式分区,请包含一个额外的补丁程序:
-
49936b4c0109411fdd38bd4ba3a32a01c40439a9 -“libavb:支持在分区开头存放 vbmeta blob。”
注意:以前,在
AvbOps
中实现get_size_of_partition
是可选的。在此更改之后,则变成了必需的,并且引导加载程序也必须实现此函数。
内核命令行更改
必须在内核命令行中添加新参数 androidboot.boot_devices
。init
使用它来启用 /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_GROUPS
、BOARD_group_SIZE
和BOARD_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 分区块设备。例如,如果设备已经有 system
和 vendor
分区,并且您希望将它们作为存储动态分区区段的块设备,则应将它们的 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.img
、vendor.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.img
和 super_vendor.img
。系统会将这些映像存放在 target_files.zip
的 OTA 文件夹中。
设备映射的存储设备调整
动态分区可以容纳许多不确定的设备映射对象。它们可能不会像预期的那样全部实例化,因此您必须跟踪所有mounts,并更新所有底层存储涉笔关联分区的Android属性。
Init中有一个机制可以跟踪mounts并异步更新Android属性。但它花费的时间不能保证在特定时间内,因此必须为所有on-property
的触发提供足够的时间。这些属性为dev.mnt.blk.<partition>
,其中<partition>
为root
、system
、data
或vendor
。每个属性都与存储设备名称关联,如以下所示:
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
时才处于活动状态,因此在初始启动阶段不能使用它们来处理root
、system
、或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`
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。