4. 动态分区配置与构建

动态分区(Dynamic Partitions)把 system、vendor、product、system_ext 等只读分区从物理分区表中“解放”出来,统一装进一个名为 super 的物理分区,由 liblp 在运行时按逻辑分区元数据动态映射。因此,构建阶段的核心工作不再是“给每个分区划固定大小”,而是回答三个问题:super 要多大、逻辑分区怎么分组、元数据怎么描述。这三件事分别落在 BoardConfig.mk、分区组配置和 fstab 上。

一、核心原理:super 是容器,逻辑分区是内容

在 Virtual A/B 方案中,super 分区同时承载 A、B 两个槽的逻辑分区。设备启动时,bootloader 把 super 的元数据交给 liblp,由 dm-linear 把逻辑分区映射成 /dev/block/mapper/system 等设备节点。因此 super 的容量必须同时满足:当前槽所有逻辑分区之和、另一槽的逻辑分区之和、以及元数据与对齐开销。

super 大小计算的基本公式为:

super_size ≥ (A槽逻辑分区总和 + B槽逻辑分区总和)
             + 元数据区(metadata) + 备份元数据(backup)
             + 对齐与预留余量

其中:
  元数据区 = 2 × metadata_size(主+备)
  对齐余量 = 每个分区按 1MB 对齐后的向上取整损耗
  预留余量 = 建议 ≥ 10%,为后续 OTA 增长留空间

如果 super 太小,OTA 会因“no space left”失败;如果 super 太大,会挤占 userdata 空间。因此它是一次容量与升级能力的权衡。

super 分区容量构成与逻辑分区组映射 物理分区 super(例如 6.5 GB) metadata 主+备 Slot A 逻辑分区组 system / vendor / product / system_ext Slot B 逻辑分区组 system / vendor / product / system_ext super_size ≥ A组 + B组 + 元数据 + 对齐余量 + 预留余量 分区组(partition group)与逻辑分区映射 group: qti_dynamic_partitions system system_ext product group: qti_dynamic_partitions_b system_b system_ext_b product_b group: vendor_group vendor vendor_dlkm odm

二、分区组:把逻辑分区按“更新边界”归类

分区组不是物理概念,而是 OTA 的更新边界。同一组内的逻辑分区在 OTA 时作为一个整体被快照和写入,因此组内分区必须共享相同的更新策略。常见做法是:

  • qti_dynamic_partitions:承载 system、system_ext、product,随系统镜像一起更新。
  • vendor_group:承载 vendor、odm、vendor_dlkm,随 vendor 镜像更新,与系统解耦。
  • B 槽组:A/B 设备中,B 槽分区通常单独成组,命名带 _b 后缀,便于独立快照。

分组错误会导致 OTA 时“跨组快照”,出现 snapshot merge failed 或分区大小不匹配。因此分组必须与 PRODUCT_USE_DYNAMIC_PARTITIONS、BOARD_SUPER_PARTITION_GROUPS 保持一致。

三、分区表配置:BoardConfig 与 fstab 的分工

构建配置分两层:BoardConfig.mk 决定“super 多大、有哪些组、每组上限多少”;fstab 决定“运行时怎么挂载这些逻辑分区”。两者必须参数一致,否则会出现“编译通过、开机挂载失败”。

配置项 所在文件 作用 典型值
BOARD_SUPER_PARTITION_SIZE BoardConfig.mk super 物理分区总大小 6442450944(6 GB)
BOARD_SUPER_PARTITION_GROUPS BoardConfig.mk 声明分区组名称列表 qti_dynamic_partitions vendor_group
BOARD_<GROUP>_PARTITION_LIST BoardConfig.mk 组内逻辑分区清单 system system_ext product
BOARD_<GROUP>_SIZE BoardConfig.mk 该组容量上限 4294967296
BOARD_SUPER_PARTITION_METADATA_DEVICE BoardConfig.mk 元数据存放设备 super
logical_partition 条目 fstab 运行时逻辑分区挂载点 /dev/block/mapper/system

一个典型的 BoardConfig 片段如下:

BOARD_SUPER_PARTITION_SIZE := 6442450944
BOARD_SUPER_PARTITION_GROUPS := qti_dynamic_partitions vendor_group

BOARD_QTI_DYNAMIC_PARTITIONS_SIZE := 4294967296
BOARD_QTI_DYNAMIC_PARTITIONS_PARTITION_LIST := \
    system system_ext product

BOARD_VENDOR_GROUP_SIZE := 1610612736
BOARD_VENDOR_GROUP_PARTITION_LIST := \
    vendor odm vendor_dlkm

BOARD_SUPER_PARTITION_METADATA_DEVICE := super
BOARD_SUPER_PARTITION_BLOCK_DEVICES := super

对应的 fstab 逻辑分区条目:

system /system ext4 ro,barrier=1 wait,logical,first_stage_mount
vendor /vendor ext4 ro,barrier=1 wait,logical,first_stage_mount
product /product ext4 ro,barrier=1 wait,logical,first_stage_mount
fstab 中的 logical 标志告诉 first-stage init:该分区不是物理分区,需要先由 liblp 从 super 元数据中映射出 /dev/block/mapper/<name>,再执行挂载。

四、案例与复盘:一次 super 扩容引发的 OTA 失败

某项目在 Android 13 上升级到 Android 14,新增了 vendor_dlkm 并扩大了 product。构建时只改了 BOARD_QTI_DYNAMIC_PARTITIONS_SIZE,却忘了同步 BOARD_SUPER_PARTITION_SIZE。结果:

  1. 编译阶段 build_super_image 报 super partition size too small,因为 A+B 两组之和已超过 super 容量。
  2. 临时把 super 调大后编译通过,但 OTA 包生成时 ota_from_target_files 提示 not enough space for snapshot,原因是预留余量不足 10%。
  3. 最终把 super 从 6 GB 调到 7 GB,并给 qti_dynamic_partitions 增加 512 MB 余量,OTA 才通过。
常见误区:只改组大小不改 super 总大小;A/B 两组共用 super 却按单槽计算;fstab 中漏写 logical 导致挂载物理分区失败;分区组命名与 BOARD_*_PARTITION_LIST 前缀不一致,导致 liblp 找不到组。

复盘结论:super 大小、分区组、BoardConfig、fstab 是同一套参数在四个位置的投影,任何一处改动都必须同步校验。建议在构建脚本中加入一致性检查:sum(group_size) + metadata + reserve ≤ super_size,并在 fstab 与 BoardConfig 之间做分区名交叉比对,把问题拦截在编译阶段,而不是留到 OTA 或开机阶段。