Skip to content

系统镜像结构

解释安装目录、文件系统镜像、boot 家族、super 动态分区、AVB 与 first-stage mount 的关系,并给出从产物追踪到运行时消费者的方法。

基于android-17.0.0_r1
AndroidAOSP系统镜像动态分区AVB

系统镜像结构 ​

在 AOSP 中看到 out/target/product/<product>/system/、system.img、super.img 和设备上的 /system 时,初学者很容易把它们都理解成“system 分区”。实际上它们分别处于安装目录、文件系统 镜像、逻辑分区容器和运行时挂载点四个层次。

模块化编译 已经说明单模块产物怎样进入 output tree; 编译错误排查 说明怎样定位失败 action。本文继续回答:这些安装产物怎样 被打包成镜像,启动时又怎样从块设备变成进程可读取的目录。

本文不展开完整刷机和 OTA payload,也不假定每台设备都具有相同分区。Android 17 源码定义的是一套 可配置机制;产品和 BoardConfig 决定最终启用哪些镜像、使用什么文件系统以及怎样组织 A/B slot。 读完后,读者应能从一个安装路径追到镜像构建规则、AVB footer、super 中的逻辑分区和 first-stage mount 消费者,并能区分“文件进入 staging 目录”“镜像生成成功”和“设备启动后挂载生效”。

1. 对象边界 ​

1.1 安装目录 ​

构建模块先把文件安装到 PRODUCT_OUT 下的目录树,例如:

text
out/target/product/<product>/
├── system/
├── system_ext/
├── product/
├── vendor/
├── odm/
├── vendor_dlkm/
└── root/

这些目录是镜像的输入,不是设备分区。adb sync system 也从这里选择待同步文件。

1.2 镜像文件 ​

system.img、vendor.img 等通常包含 ext4、EROFS 或 F2FS 文件系统。镜像内部记录目录项、inode、 权限、SELinux label、块布局,并可能带 Android sparse 表示或 AVB hashtree/footer。

1.3 物理/逻辑分区 ​

物理分区由底层存储分区表或 bootloader 暴露,例如 boot_a、vbmeta_a、super。动态分区设备还会 根据 super 中的 LP metadata 创建 system_a、vendor_a 等逻辑分区。逻辑分区通常表现为 device-mapper 块设备,不要求每个逻辑分区在 GPT 中占有独立固定区间。

1.4 挂载点 ​

/system、/vendor、/product 是运行时目录。first-stage init 读取 fstab,找到块设备、建立 dm-linear/dm-verity 映射,再把文件系统挂载到这些路径。

1.5 聚合镜像 ​

super.img 是用于初始化或刷写动态分区布局的聚合产物。它包含 LP metadata,并可携带逻辑分区的 数据 extent;它本身不是挂载到 /super 的普通文件系统。

2. 镜像家族 ​

2.1 挂载镜像 ​

镜像常见挂载点主要所有权典型消费者
system.img/system 或 system-as-root 中的 /平台核心init、framework、system_server、系统 native 服务
system_ext.img/system_ext平台扩展与 system 紧密协作但需要独立交付的系统组件
product.img/product产品配置产品 APK、资源、overlay 和系统侧产品扩展
vendor.img/vendorvendor 实现HAL 服务、vendor native 库、VINTF、vendor 配置
odm.img/odm具体设备/板级差异ODM HAL、配置与覆盖
system_dlkm.img/system_dlkmsystem kernel module 集合通用或 system 侧内核模块加载流程
vendor_dlkm.img/vendor_dlkmvendor kernel module 集合设备相关内核模块
odm_dlkm.img/odm_dlkmODM kernel module 集合更具体的板级内核模块
userdata.img/data运行时可写数据应用数据、用户数据、系统持久化状态
metadata.img/metadata启动与更新元数据FBE、Virtual A/B snapshot、DSU 等早期流程

这张表描述职责,不保证某个产品一定构建独立镜像。例如 vendor 内容可以在旧布局中位于 /system/vendor,但当 TARGET_COPY_OUT_VENDOR=vendor 时才使用独立 vendor image。判断具体设备应看 产品配置、target files 和运行时 fstab。

2.2 启动镜像 ​

镜像主要内容运行时角色
boot.imgkernel,以及按配置存在的 generic ramdiskbootloader 加载内核和早期用户空间
init_boot.imggeneric ramdisk将通用 first-stage ramdisk 从 boot 中独立出来
vendor_boot.imgvendor ramdisk、DTB、vendor cmdline、bootconfig、ramdisk fragments为设备启动提供 vendor 侧早期内容
vendor_kernel_boot.img可选 vendor kernel ramdisk 与 DTB进一步拆分 vendor kernel 启动内容
dtbo.imgdevice tree overlaysbootloader/kernel 选择设备树覆盖
recovery.imgrecovery kernel/ramdisk,具体布局受产品条件控制recovery 启动环境
vbmeta.imgAVB descriptors、签名和 chained vbmeta 引用Verified Boot 信任根和验证关系

不能把这些文件当作 ext4 镜像直接 mount。应使用 unpack_bootimg、avbtool info_image 或对应格式 工具读取。

3. 文件系统镜像生成 ​

3.1 镜像输入 ​

build_image.py 的 BuildImage 接收:

  • in_dir:分区 staging 目录;
  • property dictionary:文件系统、分区大小、SELinux、AVB、稀疏格式等配置;
  • out_file:最终 .img;
  • target_out:供 fs_config 查设备权限配置的 system 输出目录。

核心入口先处理 system-as-root,再选择文件系统和 verity builder:

源码文件:build/make/tools/releasetools/build_image.py

相关函数/类型:BuildImage

python
in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)
SetUUIDIfNotExist(prop_dict)

fs_type = prop_dict.get("fs_type", "")

# 说明:EROFS和压缩F2FS不一定用文件系统内容填满整个partition。
fs_spans_partition = True
if fs_type.startswith("erofs"):
    fs_spans_partition = False
elif fs_type.startswith("f2fs") and prop_dict.get("f2fs_compress") == "true":
    fs_spans_partition = False

verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)

property dictionary 来自产品全局信息和分区专属变量。ImagePropFromGlobalDict 支持的只读分区包括 system、vendor、product、system_ext、odm 和三个 dlkm 分区;data 则使用可写文件系统配置。

3.2 文件系统格式 ​

BuildImageMkfs 根据 fs_type 选择工具:

源码文件:build/make/tools/releasetools/build_image.py

相关函数/类型:BuildImageMkfs

python
if fs_type.startswith("ext"):
    build_command = [prop_dict["ext_mkuserimg"]]
    build_command.extend([
        in_dir, out_file, fs_type,
        prop_dict["mount_point"],
        prop_dict["image_size"],
    ])
elif fs_type.startswith("erofs"):
    build_command = ["mkfs.erofs"]
    build_command.extend([
        "--mount-point", prop_dict["mount_point"],
    ])
elif fs_type.startswith("f2fs"):
    build_command = ["mkf2fsuserimg"]
    build_command.extend([
        "-t", prop_dict["mount_point"],
    ])
else:
    raise BuildImageError(
        "Error: unknown filesystem type: {}".format(fs_type)
    )

选择文件系统不是“所有 system 都是 EROFS、所有 data 都是 F2FS”的固定规则。BoardConfig 和产品变量 决定最终值,设备内核也必须支持该文件系统及所需 feature。

3.3 SELinux输入 ​

目录中的 Unix mode 不是完整权限来源。构建还会使用 fs_config、设备专属配置和 file contexts, 把 uid/gid、capabilities 与 SELinux label 写入镜像。只复制文件到 staging 目录但漏掉配置,最终设备上 可能出现权限、执行或 SELinux 访问失败。

3.4 system-as-root ​

构建 system image 时,脚本会创建临时根目录,把 root staging 与 system/ 合并,并把 mount point 改为 /:

源码文件:build/make/tools/releasetools/build_image.py

相关函数/类型:SetUpInDirAndFsConfig

python
if prop_dict["mount_point"] != "system":
    return origin_in, fs_config

in_dir = common.MakeTempDir()
root_dir = prop_dict.get("root_dir")
if root_dir:
    shutil.rmtree(in_dir)
    shutil.copytree(root_dir, in_dir, symlinks=True)

in_dir_system = os.path.join(in_dir, "system")
shutil.copytree(origin_in, in_dir_system, symlinks=True)

# 说明:输出镜像表示根文件系统布局,而不是只表示/system子目录。
prop_dict["mount_point"] = "/"

test_SetUpInDirAndFsConfig 构造 root 中的 init、system 文件和 symlink,断言合并后的 staging 同时 包含根内容和 /system 内容,并确认 mount point 变为 /。这解释了为什么“打开 system.img 后最外层 不是预期目录”不能直接判定构建错误。

4. 镜像大小边界 ​

4.1 镜像大小 ​

静态分区配置通常给出 partition_size。动态分区可以启用 use_dynamic_partition_size,让脚本根据 目录或压缩后镜像大小计算空间,再加入 reserved、headroom、alignment 和 AVB footer 所需空间。

源码文件:build/make/tools/releasetools/build_image.py

相关函数/类型:BuildImage

python
if (prop_dict.get("use_dynamic_partition_size") == "true" and
        "partition_size" not in prop_dict):
    if fs_type.startswith("erofs"):
        # 说明:压缩文件系统先生成,再按实际压缩尺寸估算。
        mkfs_output = BuildImageMkfs(...)
        size = GetDiskUsage(out_file)
    else:
        size = GetDiskUsage(in_dir)

    size = CalculateSizeAndReserved(prop_dict, size)
    size = common.RoundUpTo4K(size)

4.2 headroom意义 ​

headroom 是镜像内容之外仍需保留的空间,不等于 host 磁盘剩余量。ext4 构建后会解析 mke2fs 输出, 确认空闲 blocks 能满足 partition_headroom。test_CheckHeadroom_InsufficientHeadroom 把要求从 1000 blocks 提高到 1001 blocks,断言构建抛出 BuildImageError。

如果镜像大小超限,正确入口是检查文件增长、重复安装、文件系统压缩、reserved/headroom、AVB footer 与逻辑分区 group 上限,而不是只增大一个随机常量。

4.3 sparse 格式 ​

Android sparse image 是传输/存储表示,用 chunk 描述 raw 数据、填充值和跳过区间。一个 sparse system.img 内部仍可以是 ext4。分析工具若要求 raw image,需要先转换;但对支持 Android sparse 的 fastboot 或构建工具,不应无条件转换后再刷写。

5. 启动镜像 ​

5.1 启动镜像构建 ​

Android 17 的 board_config.mk 不假定所有产品都有三张镜像:

  • boot 是否构建受 PRODUCT_BUILD_BOOT_IMAGE、kernel 和 recovery-as-boot 影响;
  • 存在 BOARD_INIT_BOOT_IMAGE_PARTITION_SIZE 或产品显式开启时构建 init_boot;
  • BOARD_BOOT_HEADER_VERSION >= 3 时默认构建 vendor_boot;
  • vendor ramdisk fragments 要求 vendor_boot,并且 header version 4 才允许相关配置;
  • 还可能使用 prebuilt boot、prebuilt init_boot 或 GKI APEX 提供的 boot image。

所以“缺少 init_boot.img”可能只是产品没有启用该分区,而不是构建漏产物。

5.2 ramdisk ​

boot image 规则在没有 init_boot 时把 INSTALLED_RAMDISK_TARGET 加入 boot;启用 init_boot 后,这个 ramdisk 从 boot 参数中移除:

源码文件:build/make/core/Makefile

相关函数/类型:INTERNAL_BOOTIMAGE_ARGS

makefile
INTERNAL_BOOTIMAGE_ARGS := \
    $(addprefix --second ,$(INSTALLED_2NDBOOTLOADER_TARGET))

ifneq ($(BUILDING_INIT_BOOT_IMAGE),true)
  # 说明:没有init_boot时,generic ramdisk仍进入boot。
  INTERNAL_BOOTIMAGE_ARGS += --ramdisk $(INSTALLED_RAMDISK_TARGET)
endif

init_boot 则明确使用 generic ramdisk:

源码文件:build/make/core/Makefile

相关函数/类型:init_boot rule

makefile
INSTALLED_INIT_BOOT_IMAGE_TARGET := $(PRODUCT_OUT)/init_boot.img
INTERNAL_INIT_BOOT_IMAGE_ARGS := --ramdisk $(INSTALLED_RAMDISK_TARGET)

$(MKBOOTIMG) $(INTERNAL_INIT_BOOT_IMAGE_ARGS) \
    $(INTERNAL_MKBOOTIMG_VERSION_ARGS) \
    $(BOARD_MKBOOTIMG_INIT_ARGS) \
    --output "$@"

5.3 mkbootfs ​

vendor_boot 规则从 TARGET_VENDOR_RAMDISK_OUT 收集文件,用 mkbootfs 生成 vendor ramdisk,还可加入:

  • DTB;
  • vendor kernel cmdline;
  • vendor bootconfig;
  • 命名 vendor ramdisk fragments;
  • 按配置迁移的 recovery resources;
  • GSI AVB keys 等设备启动输入。

header version 4 的 ramdisk table 允许多个 fragment 带 name/type。fastboot 的 vendor_boot_img_utils_test.cpp 使用不同 header fixture 重打包 DTB、bootconfig 和 fragments,并检查 结果及 AVB footer 保持,证明工具必须按 header 结构处理,不能把 vendor_boot 当成“一个固定 cpio”。

6. super容器 ​

6.1 LP metadata ​

LP metadata 至少描述:

  • super metadata device 与实际 block devices;
  • metadata slot 数;
  • partition groups 及其最大空间;
  • 逻辑 partition 名称、属性与所属 group;
  • 每个 partition 对应的 extent;
  • Virtual A/B 等 header flags。

逻辑分区可以由多个 extent 组成,extent 指向 super block device 上的区间。运行时通过 device-mapper 把这些不必连续的区间映射为一个线性块设备。

6.2 配置 ​

build_super_image.py 把 misc_info 中的动态分区信息转换为 lpmake 命令:

源码文件:build/make/tools/releasetools/build_super_image.py

python
# 符号:BuildSuperImageFromDict
cmd = [
    info_dict["lpmake"],
    "--metadata-size", "65536",
    "--super-name", info_dict["super_metadata_device"],
]

if ab_update:
    cmd += ["--metadata-slots", "3"]
else:
    cmd += ["--metadata-slots", "2"]

if virtual_ab:
    cmd.append("--virtual-ab")

for device in block_devices:
    cmd += ["--device", "{}:{}".format(device, size)]

group 和 partition 随后加入。A/B 模式为 group 创建 _a/_b,初始化 super image 时把正常 image 填入 partition_a,B slot 通常为空;如果存在 system_other.img,它可以作为 system B slot 输入。

6.3 group空间 ​

group 不是 mount point,也不是目录。它给一组逻辑分区设置总大小上限,OTA 调整 system/vendor 等 大小时必须保持 group 约束。单个 image 能成功生成,不表示它和同组其他 image 一定能装入 super。

6.4 super布局 ​

产物LP metadata分区数据主要用途
super.img有可包含 system/vendor/product 等 image初始化或整体刷写动态分区布局
super_empty.img有逻辑分区通常为空建立空布局,供后续逐分区写入或测试

构建 super.img 还需要 PRODUCT_BUILD_SUPER_PARTITION=true 和 super size/config。源码同时提供 dist 版本、开发版本和 nodeps 入口,不能仅凭 output tree 中缺少 super.img 判断动态分区未启用。

SuperFlashHelper.ImageEquality 读取 super_empty.img,加入 system_a image 后生成 sparse layout,再 逐字节与 lpmake 生成的 super.img 比较;除 lpmake 对 super 总大小追加的零填充外,内容必须一致。 这个测试反向验证了“empty metadata + logical image → super layout”的组合关系。

7. first-stage init ​

7.1 分区映射 ​

FirstStageMount::Create 读取默认 fstab,只保留 first_stage_mount entry。只要某个 entry 带 logical flag,IsDmLinearEnabled() 就返回 true,init 会寻找 super block device。

7.2 metadata顺序 ​

Android first-stage mount 会先尝试挂载 /metadata,因为 Virtual A/B snapshot merge 等状态可能影响 逻辑分区应映射到原始 extent 还是 snapshot/COW 设备:

源码文件:system/core/init/first_stage_mount_android.cpp

cpp
// 符号:FirstStageMountAndroid::DoCreateDevices
auto metadata_partition = std::find_if(
        fstab_.begin(), fstab_.end(),
        [](const auto& entry) {
            return entry.mount_point == "/metadata";
        });

if (metadata_partition != fstab_.end()) {
    MountPartition(metadata_partition, true);
}

// 说明:snapshot状态可改变后续逻辑分区映射。
if (!CreateLogicalPartitions()) return false;

7.3 dm-linear ​

普通动态分区路径会读取当前 slot 的 LP metadata,初始化 backing devices,再调用 CreateLogicalPartitions:

源码文件:system/core/init/first_stage_mount_android.cpp

cpp
// 符号:FirstStageMountAndroid::CreateLogicalPartitions
auto metadata = android::fs_mgr::ReadCurrentMetadata(super_path_);
if (!metadata) {
    LOG(ERROR) << "Could not read logical partition metadata from "
               << super_path_;
    return false;
}
if (!InitDmLinearBackingDevices(*metadata.get())) {
    return false;
}

// 说明:LP extents在这里变成可供fstab使用的device-mapper设备。
return android::fs_mgr::CreateLogicalPartitions(
        *metadata.get(), super_path_);

Virtual A/B 更新进行中时,流程可能改为 snapshot mapping,并启动 snapuserd。此时逻辑分区看到的内容 可能由 base partition 与 COW snapshot 合成,不能只看 super 原始 extent 判断当前读到的数据。

7.4 AVB后才挂载 ​

MountPartition 对 logical entry 先更新逻辑块设备并等待 dm device,再建立 dm-verity/AVB,最后执行 文件系统 mount:

源码文件:system/core/init/first_stage_mount.cpp

cpp
// 符号:FirstStageMount::MountPartition
if (begin->fs_mgr_flags.logical) {
    if (!fs_mgr_update_logical_partition(&(*begin))) {
        return false;
    }
    if (!block_dev_init_.InitDmDevice(begin->blk_device)) {
        return false;
    }
}
if (!SetUpDmVerity(&(*begin))) {
    return false;
}

bool mounted = (fs_mgr_do_mount_one(*begin) == 0);

同一 mount point 可以有多个候选 entry,前一种文件系统挂载失败后会尝试后续 entry;但非 no_fail、 非 formattable 的关键分区最终失败时,first-stage mount 返回 false,启动不会假装成功继续。

8. AVB信任 ​

8.1 mkbootimg ​

boot、init_boot、vendor_boot 等整体读取的镜像通常添加 hash footer;system、vendor、product 等大文件 系统镜像通常使用 hashtree,使运行时能够按块验证。具体算法、key 和 rollback index 由 BoardConfig 决定。

boot 构建规则先运行 mkbootimg,检查最大尺寸,再调用:

源码文件:build/make/core/Makefile

相关函数/类型:AVB hash footer

makefile
$(AVBTOOL) add_hash_footer \
    --image $(1) \
    --partition_name boot \
    $(INTERNAL_AVB_BOOT_SIGNING_ARGS)

init_boot 和 vendor_boot 也有各自 partition name 与 signing args。文件系统镜像的 property dictionary 则让 CreateVerityImageBuilder 预留 footer/hashtree 空间并在 mkfs 后完成 verified image。

8.2 vbmeta 验证 ​

vbmeta.img 保存 descriptors、签名、公钥关系和 chained vbmeta 信息,不复制 system/vendor 文件。 构建规则还会为 system、product、system_ext、boot、init_boot、vendor 等 descriptor 加入 OS version、 fingerprint 或 security patch properties。

8.3 验证失败 ​

first-stage mount 的 SetUpDmVerity 失败时不会继续挂载关键分区。设备解锁状态、允许的验证模式、 rollback index 和 chained vbmeta 配置会影响处理结果,但不能把“bootloader 已加载 kernel”理解成 所有系统分区都已通过验证。

9. A/B ​

9.1 A/B是slot集合 ​

A/B 设备通常有 slot-suffixed boot chain 分区,并在 super metadata 中描述 slot-suffixed logical partitions。build_super_image.py 对 A/B group 创建 _a/_b,metadata slot 数从 2 变为 3。

当前 slot 由 boot control 与 bootloader 选择。查看运行时布局时应同时记录:

bash
adb shell getprop ro.boot.slot_suffix
adb shell bootctl get-current-slot
adb shell ls -l /dev/block/by-name

并非所有命令在每个产品都预装或允许执行,slot suffix 和实际 partition name 应以设备输出为准。

9.2 Slot元数据 ​

Virtual A/B 利用 snapshot/COW 保存更新差异,在新 slot 启动和 merge 期间由 dm-snapshot、snapuserd 等 组合数据。super 仍提供 base logical partition layout;/metadata 和 /data 上的更新状态/映像参与 first-stage 映射。

因此,更新过程中 lpdump super、当前 dm table 与挂载后文件内容可能回答不同问题:前者描述基础 LP layout,dm table 描述当前映射,文件系统内容描述消费者实际看到的数据。

10. 构建产物检查 ​

10.1 产品产物 ​

bash
product_out="$(get_build_var PRODUCT_OUT)"
find "$product_out" -maxdepth 1 -name '*.img' -print | sort

不要用教程中的固定列表判断缺失。产品可能不构建 cache、recovery、init_boot、vendor_boot、super, 也可能增加 vendor_kernel_boot 或自定义镜像。

10.2 判断镜像类型 ​

bash
file "$product_out/system.img"
file "$product_out/boot.img"

avbtool info_image --image "$product_out/system.img"
unpack_bootimg --boot_img "$product_out/boot.img" --out /tmp/boot-unpacked
lpdump "$product_out/super.img"

file 只能提供初步识别。sparse image、AVB footer 和自定义容器会让输出不直观;应结合专用工具。

10.3 super提取 ​

bash
lpunpack "$product_out/super.img" /tmp/super-unpacked
find /tmp/super-unpacked -maxdepth 1 -type f -print | sort

A/B 初始化 super 可能同时出现 _a/_b,B slot image 也可能为空。提取结果应结合 lpdump 中的 group、 extent 和 attributes 解读。

10.4 simg2img ​

对 ext4 raw image 可以在 Linux 上只读 loop mount;EROFS 应使用内核支持或对应工具。Android sparse 需要先确认工具是否支持,必要时用 simg2img 转为 raw。任何 mount 或提取结果都只代表该 image, 不自动代表设备当前 slot 和 snapshot 映射正在使用它。

11. 构建与运行边界 ​

11.1 adb sync ​

adb sync system 从 ANDROID_PRODUCT_OUT/system 同步到设备 /system 视图。它不重打 boot.img, 也不重新生成 super LP metadata。是否能写入取决于 adbd root、remount、verity、overlayfs 与产品策略。

11.2 remount覆盖 ​

first-stage mount 在基础分区完成后还会处理 overlay entries 和 scratch partition。调试设备上 adb remount 可能通过 overlayfs 让只读分区呈现可写视图。此时文件修改可以立即被进程加载,但底层 system.img 或 super extent 未必变化,重启、禁用 overlay 或重新刷写后可能恢复。

11.3 车机匹配 ​

设备能够 adb root 和 adb remount,只能证明当前车机开放调试能力。若它运行厂商镜像而不是当前 AOSP product,主机生成的模块、分区布局、签名、ABI、VINTF、APEX 和 overlay 可能不匹配。部署前至少 要把构建 product、设备 build fingerprint、slot、目标 mount point 与实际消费者对应起来。

完整 fastboot/fastbootd、分区选择和恢复方案属于后续刷机专题。系统镜像结构文章只负责说明目标产物 是什么以及运行时怎样消费。

12. 常见判断错误 ​

12.1 system.img ​

可能只构建了模块目标,或 product 使用 Soong-defined filesystem、target files 阶段生成、system-only 输出等路径。先查 Ninja target、PRODUCT_OUT 和产品配置。

12.2 super内容 ​

super 的首要结构是 LP metadata 和 extent。需要先用 lpdump/lpunpack 找到逻辑分区 image,再按文件 系统格式查看内容。

12.3 vendor边界 ​

vendor 是分区 ABI 与所有权边界,不是“凡是厂商添加的文件都放这里”。产品 APK 可能属于 product, 设备差异可能属于 odm,kernel modules 可能属于 vendor_dlkm/odm_dlkm,boot-time 内容可能属于 vendor_boot。

12.4 boot.img组成 ​

启用 init_boot 时 generic ramdisk 不在 boot;GKI、vendor_boot、vendor_kernel_boot、recovery-as-boot 等配置也会改变内容。必须先读 header version 和产品变量。

12.5 remount结果 ​

remount 改变的是当前挂载视图;overlayfs、snapshot 和 slot 都可能使它与物理 image 不同。重启后是否 保留、AVB 是否仍成立、OTA 是否会覆盖,需要分别验证。

13. 源码入口 ​

理解镜像结构的有效方式不是背分区表,而是选择一个真实文件,沿所有权反向追踪。

文件或修改staging目录镜像/逻辑分区运行时消费者
services.jar 中的 framework 修改PRODUCT_OUT/system/frameworksystem.img → system[_slot]system_server
vendor HAL servicePRODUCT_OUT/vendor/bin/hwvendor.img → vendor[_slot]init 启动的 HAL 进程
产品 overlay/APKPRODUCT_OUT/productproduct.img → product[_slot]ResourceManager 或应用进程
vendor kernel modulePRODUCT_OUT/vendor_dlkm/lib/modulesvendor_dlkm.imgearly/late module loader
first-stage vendor rc/模块vendor ramdisk stagingvendor_boot.img fragmentfirst-stage init

对每个对象都应回答:谁把它安装到 staging,谁选择文件系统和 image size,是否进入 super,fstab 如何 找到它,AVB 验证哪一层,最后哪个进程或内核路径读取它。

以 framework 修改为例,完整链路不是“编译 services.jar 后刷 system.img”这么短:

text
framework源码
  -> services模块与installed output
  -> PRODUCT_OUT/system/framework/services.jar
  -> build_image生成system.img并加入AVB数据
  -> build_super_image将system image放入逻辑分区extent
  -> first-stage init创建dm-linear/dm-verity设备
  -> mount为/system
  -> zygote/system_server加载新的framework代码

如果设备通过 overlayfs 接收单文件更新,链路中“重建 system.img 与 super”可以被调试路径暂时绕过, 但文件的逻辑归属、签名边界和最终消费者没有改变。能够把单个文件沿这条链路定位清楚,才真正理解了 Android 系统镜像结构。