android 系统相关

android-13 super 分区挂载流程



前言

从 androidd 10 开始,google 将 vendor/system/product 分区整合到一个 super 分区中,编译后的最终镜像不再有这些单独的 image,取而代之的是一个总的 super.img。
本文基于 android-13_r1 版本、使能 A/B 分区为基础,介绍了 super 分区的结构挂载以及编译的整个流程。


一、为什么用 super 分区代替独立的分区?

  • 传统的分区方式将 system、vendor、product、分区各自独立,在实际的项目开发中,通常每个分区都会留有部分空间,三个(可能没有 product) 分区累计就需要预留不少的空间,并且各个分区的大小被固定,不能灵活调整,比如 system 分区容量不够了,但是 vendor 分区又有多余的空间。
  • 统一合并成一个分区后,使用 super 动态调整分区功能可以灵活调整内部分区大小,从而能更好的利用存储空间。

二、super 分区结构

图 1. 转换为动态分区时的新物理分区表布局
支持的动态分区包括:

  • 系统
  • Vendor
  • Product
  • System Ext
  • ODM

对于 A/B 设备,super 分区的大小需要包括两个槽位的大小,super 分区会在内部处理 A/B 槽位,因此 A/B 设备不需要单独的 super_a 和 super_b 分区。

通过下面的命令可以 dump 出 super.img 的结构:

  • 下面的信息显示此 super.img 包含 odm_dlkm_a/b、product_a/b、system_a/b、vendor_a/b、vendor_dlkm_a/b 共 10 个子分区。
  • Super partition layout 这一段描述了各个分区的大小
## lpdump super_raw.img 
xxx@xxxx:/Android-13$ lpdump super_raw.img 
Slot 0:
Metadata version: 10.2
Metadata size: 1128 bytes
Metadata max size: 65536 bytes
Metadata slot count: 3
Header flags: virtual_ab_device
Partition table:    // 这里列出了 super 中包含了哪些分区
------------------------
  Name: odm_dlkm_a
  Group: main_a
  Attributes: readonly
  Extents:
    0 .. 679 linear super 2048
------------------------
  Name: odm_dlkm_b
  Group: main_b
  Attributes: readonly
  Extents:
------------------------
  Name: product_a
  Group: main_a
  Attributes: readonly
  Extents:
    0 .. 4617239 linear super 4096
------------------------
  Name: product_b
  Group: main_b
  Attributes: readonly
  Extents:
------------------------
  Name: system_a
  Group: main_a
  Attributes: readonly
  Extents:
    0 .. 3241831 linear super 4622336
------------------------
  Name: system_b
  Group: main_b
  Attributes: readonly
  Extents:
    0 .. 279991 linear super 7864320
------------------------
  Name: vendor_a
  Group: main_a
  Attributes: readonly
  Extents:
    0 .. 1562079 linear super 8144896
------------------------
  Name: vendor_b
  Group: main_b
  Attributes: readonly
  Extents:
------------------------
  Name: vendor_dlkm_a
  Group: main_a
  Attributes: readonly
  Extents:
    0 .. 86831 linear super 9707520
------------------------
  Name: vendor_dlkm_b
  Group: main_b
  Attributes: readonly
  Extents:
------------------------
Super partition layout:     // 这里列出了各个分区的的大小及 offset,在创建 dm-linear 节点时需要此信息
------------------------
super: 2048 .. 2728: odm_dlkm_a (680 sectors)
super: 4096 .. 4621336: product_a (4617240 sectors)
super: 4622336 .. 7864168: system_a (3241832 sectors)
super: 7864320 .. 8144312: system_b (279992 sectors)
super: 8144896 .. 9706976: vendor_a (1562080 sectors)
super: 9707520 .. 9794352: vendor_dlkm_a (86832 sectors)
------------------------
Block device table:   // 此分区(super)的大小
------------------------
  Partition name: super
  First sector: 2048
  Size: 9663676416 bytes
  Flags: none
------------------------
Group table:     // 2 个 group (就是 2 个 solt,就是A/B solt,推测不确定)
------------------------
  Name: default
  Maximum size: 0 bytes
  Flags: none
------------------------
  Name: main_a
  Maximum size: 9661579264 bytes
  Flags: none
------------------------
  Name: main_b
  Maximum size: 9661579264 bytes
  Flags: none
------------------------

lpdump 只能解析非 sparse 格式的 image,而系统编译的 super.img 是 sparse 格式,需要使用 simg2img 将 Android 编译生成的 sparse 格式的 super.img 转换成 raw 格式。

simg2img out/target/product/xxx/super.img super_raw.img

simg2img 的源码位于: /Android-13/system/core/libsparse/simg2img.cpp
lpdump 的源码位于: /Android-13/system/extras/partition_tools/lpdumpd.cc
编译后的路径位于:/Android-13/out/host/linux-x86/bin/

三、super 分区挂载流程

截取 fstab 文件相关的内容(只含有 first_stage_mount 属性)放在这,方便后面查找。

# cat /vendor/etc/fstab.emmc                                                                                                                                                                  
# 1 "vendor/mediatek/proprietary/hardware/fstab/mt6789/fstab.in.emmc"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 341 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "vendor/mediatek/proprietary/hardware/fstab/mt6789/fstab.in.emmc" 2
# 156 "vendor/mediatek/proprietary/hardware/fstab/mt6789/fstab.in.emmc"
system /system ext4 ro wait,slotselect,avb=vbmeta_system,logical,first_stage_mount,avb_keys=/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey

system_ext /system_ext ext4 ro wait,slotselect,avb=vbmeta_system,logical,first_stage_mount,avb_keys=/avb/q-gsi.avbpubkey:/avb/r-gsi.avbpubkey:/avb/s-gsi.avbpubkey

vendor /vendor ext4 ro wait,slotselect,avb,logical,first_stage_mount

product /product ext4 ro wait,slotselect,avb,logical,first_stage_mount

vendor_dlkm /vendor_dlkm ext4 ro wait,slotselect,avb,logical,first_stage_mount

odm_dlkm /odm_dlkm ext4 ro wait,slotselect,avb,logical,first_stage_mount

/dev/block/by-name/metadata /metadata ext4 noatime,nosuid,nodev,discard wait,check,formattable,first_stage_mount

挂载流程从 init 进程 main() 函数开始。

// Android-13/system/core/init/init.cpp
int main(int argc, char** argv) {

    // 调用 FirstStageMain() 挂载各个文件系统
    return FirstStageMain(argc, argv);
}

// Android-13/system/core/init/first_stage_init.cpp
int FirstStageMain(int argc, char** argv) {

    // ...
    // ...
    if (!DoFirstStageMount(!created_devices)) {
        LOG(FATAL) << "Failed to mount required partitions early ...";
    }

}

// Android-13/system/core/init/first_stage_mount.cpp
// Mounts partitions specified by fstab in device tree.
bool DoFirstStageMount(bool create_devices) {
    // Skips first stage mount if we're in recovery mode.
    if (IsRecoveryMode()) {
        LOG(INFO) << "First stage mount skipped (recovery mode)";
        return true;
    }

    // <1> Create 从 dtbt 或者 fstab 文件中获取需要挂载的分区
    auto fsm = FirstStageMount::Create();
    if (!fsm.ok()) {
        LOG(ERROR) << "Failed to create FirstStageMount " << fsm.error();
        return false;
    }
    // <2> 创建设备节点
    if (create_devices) {
        if (!(*fsm)->DoCreateDevices()) return false;
    }

    // <3> 执行最终的 mount 操作
    return (*fsm)->DoFirstStageMount();
}

static Result<Fstab> ReadFirstStageFstab() {
    Fstab fstab;
    // 先尝试从 dtbt 中读取
    if (!ReadFstabFromDt(&fstab)) {
        // 如果失败则从 fstab 文件中读取
        // 这里只关注有 first_stage_mount 属性的节点
        // 提个问题:此时无论是 system 还是 vendor 分区都没有挂载,而设备上只有 /vendor/etc/fstab.platform 一个文件
        // 试问, 这个 fstab 文件会在哪里呢?
        if (ReadDefaultFstab(&fstab)) {
            fstab.erase(std::remove_if(fstab.begin(), fstab.end(),
                                       [](const auto& entry) {
                                           return !entry.fs_mgr_flags.first_stage_mount;
                                       }),
                        fstab.end());
        } else {
            return Error() << "failed to read default fstab for first stage mount";
        }
    }
    return fstab;
}

bool FirstStageMount::DoCreateDevices() {
    if (!InitDevices()) return false;

    // Mount /metadata before creating logical partitions, since we need to
    // know whether a snapshot merge is in progress.
    auto metadata_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
        return entry.mount_point == "/metadata";
    });
    // 如果有 metadata 分区,先挂载它,这里有 AVB 启动需要的 key.
    if (metadata_partition != fstab_.end()) {
        if (MountPartition(metadata_partition, true /* erase_same_mounts */)) {
            // Copies DSU AVB keys from the ramdisk to /metadata.
            // Must be done before the following TrySwitchSystemAsRoot().
            // Otherwise, ramdisk will be inaccessible after switching root.
            CopyDsuAvbKeys();
        }
    }

    // 创建逻辑分区
    if (!CreateLogicalPartitions()) return false;

    return true;
}

bool FirstStageMount::CreateLogicalPartitions() {
    if (!IsDmLinearEnabled()) {
        return true;
    }
    if (super_path_.empty()) {
        LOG(ERROR) << "Could not locate logical partition tables in partition "
                   << super_partition_name_;
        return false;
    }

    if (SnapshotManager::IsSnapshotManagerNeeded()) {
        auto sm = SnapshotManager::NewForFirstStageMount();
        if (!sm) {
            return false;
        }
        if (sm->NeedSnapshotsInFirstStageMount()) {
            return CreateSnapshotPartitions(sm.get());
        }
    }

    // 这里的 super_path_ 就是 super 分区的实际路径,比如:/dev/block/mmcblk0p57
    // 通过 ReadCurrentMetadata() 从 super 分区中解析出 metadata.
    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;
    }
    // 根据上面解析出的 metadata,创建逻辑分区节点 /dev/block/dm-X
    return android::fs_mgr::CreateLogicalPartitions(*metadata.get(), super_path_);
}

// Android-13/system/core/fs_mgr/fs_mgr_dm_linear.cpp
bool CreateLogicalPartitions(const LpMetadata& metadata, const std::string& super_device) {
    CreateLogicalPartitionParams params = {
            .block_device = super_device,
            .metadata = &metadata,
    };
    // 遍历 metadata 中所有的逻辑分区
    for (const auto& partition : metadata.partitions) {
        if (!partition.num_extents) {
            LINFO << "Skipping zero-length logical partition: " << GetPartitionName(partition);
            continue;
        }
        if (partition.attributes & LP_PARTITION_ATTR_DISABLED) {
            LINFO << "Skipping disabled partition: " << GetPartitionName(partition);
            continue;
        }

        params.partition = &partition;

        // 真正的创建节点
        std::string ignore_path;
        if (!CreateLogicalPartition(params, &ignore_path)) {
            LERROR << "Could not create logical partition: " << GetPartitionName(partition);
            return false;
        }
    }
    return true;
}

// 根据 params 参数创建 dev-X 节点
// 这个函数的功能就是根据 metadata 中的分区信息创建 dm-X 节点
// 至于内部流程后续在 device-mapper 原理再另起新篇幅介绍。
bool CreateLogicalPartition(CreateLogicalPartitionParams params, std::string* path) {
    CreateLogicalPartitionParams::OwnedData owned_data;
    if (!params.InitDefaults(&owned_data)) return false;

    // 根据 params 设置 dmTable
    DmTable table;
    if (!CreateDmTableInternal(params, &table)) {
        return false;
    }

    // 调用 dm 方法创建节点
    DeviceMapper& dm = DeviceMapper::Instance();
    if (!dm.CreateDevice(params.device_name, table, path, params.timeout_ms)) {
        return false;
    }
    LINFO << "Created logical partition " << params.device_name << " on device " << *path;

    return true;
}

// 根据 params 设置 dmTable
bool CreateDmTableInternal(const CreateLogicalPartitionParams& params, DmTable* table) {
    const auto& super_device = params.block_device;

    uint64_t sector = 0;
    for (size_t i = 0; i < params.partition->num_extents; i++) {
        const auto& extent = params.metadata->extents[params.partition->first_extent_index + i];
        std::unique_ptr<DmTarget> target;
        switch (extent.target_type) {
            case LP_TARGET_TYPE_ZERO:
                target = std::make_unique<DmTargetZero>(sector, extent.num_sectors);
                break;
            case LP_TARGET_TYPE_LINEAR: { // 所有的逻辑分区都是 linear
                const auto& block_device = params.metadata->block_devices[extent.target_source];
                std::string dev_string;
                if (!GetPhysicalPartitionDevicePath(params, block_device, super_device,
                                                    &dev_string)) {
                    LOG(ERROR) << "Unable to complete device-mapper table, unknown block device";
                    return false;
                }
                target = std::make_unique<DmTargetLinear>(sector, extent.num_sectors, dev_string,
                                                          extent.target_data);
                break;
            }
            default:
                LOG(ERROR) << "Unknown target type in metadata: " << extent.target_type;
                return false;
        }
        if (!table->AddTarget(std::move(target))) {
            return false;
        }
        sector += extent.num_sectors;
    }
    if (params.partition->attributes & LP_PARTITION_ATTR_READONLY) {
        table->set_readonly(true);
    }
    if (params.force_writable) {
        table->set_readonly(false);
    }
    return true;
}

上面的整个流程 LOG 如下图所示,最终将 super 分区映射成了 6 个逻辑分区。
dm-0 dm-1 等设备的顺序由 kernel 按照顺序产生,而 ReadCurrentMetadata() 函数又是按照前后顺序解析 metadata,所以最终这些顺序就是以在 metadata 中出现的顺序产生。
到目前位置,只是创建了 dm-X 这些节点,还并没有 mount,下面继续次流程。

在这里插入图片描述

bool FirstStageMount::DoFirstStageMount() {
    if (!IsDmLinearEnabled() && fstab_.empty()) {
        // Nothing to mount.
        LOG(INFO) << "First stage mount skipped (missing/incompatible/empty fstab in device tree)";
        return true;
    }

    if (!MountPartitions()) return false;

    return true;
}

// 这里是真正的挂载流程
bool FirstStageMount::MountPartitions() {
    // 这里首先挂载 /system 分区(逻辑分区)
    if (!TrySwitchSystemAsRoot()) return false;

    if (!SkipMountingPartitions(&fstab_, true /* verbose */)) return false;

    // 遍历 fstab 中所有的逻辑节点
    for (auto current = fstab_.begin(); current != fstab_.end();) {

        // We've already mounted /system above.
        if (current->mount_point == "/system") {
            ++current;
            continue;
        }

        // Handle overlayfs entries later.
        if (current->fs_type == "overlay") {
            ++current;
            continue;
        }

        // Skip raw partition entries such as boot, dtbo, etc.
        // Having emmc fstab entries allows us to probe current->vbmeta_partition
        // in InitDevices() when they are AVB chained partitions.
        if (current->fs_type == "emmc") {
            ++current;
            continue;
        }

        Fstab::iterator end;
        if (!MountPartition(current, false /* erase_same_mounts */, &end)) {
            if (current->fs_mgr_flags.no_fail) {
                LOG(INFO) << "Failed to mount " << current->mount_point
                          << ", ignoring mount for no_fail partition";
            } else if (current->fs_mgr_flags.formattable) {
                LOG(INFO) << "Failed to mount " << current->mount_point
                          << ", ignoring mount for formattable partition";
            } else {
                PLOG(ERROR) << "Failed to mount " << current->mount_point;
                return false;
            }
        }
        current = end;
    }

    for (const auto& entry : fstab_) {
        if (entry.fs_type == "overlay") {
            fs_mgr_mount_overlayfs_fstab_entry(entry);
        }
    }

    // If we don't see /system or / in the fstab, then we need to create an root entry for
    // overlayfs.
    if (!GetEntryForMountPoint(&fstab_, "/system") && !GetEntryForMountPoint(&fstab_, "/")) {
        FstabEntry root_entry;
        if (GetRootEntry(&root_entry)) {
            fstab_.emplace_back(std::move(root_entry));
        }
    }

    // heads up for instantiating required device(s) for overlayfs logic
    auto init_devices = [this](std::set<std::string> devices) -> bool {
        for (auto iter = devices.begin(); iter != devices.end();) {
            if (android::base::StartsWith(*iter, "/dev/block/dm-")) {
                if (!block_dev_init_.InitDmDevice(*iter)) {
                    return false;
                }
                iter = devices.erase(iter);
            } else {
                iter++;
            }
        }
        return InitRequiredDevices(std::move(devices));
    };
    MapScratchPartitionIfNeeded(&fstab_, init_devices);

    fs_mgr_overlayfs_mount_all(&fstab_);

    return true;
}

// If system is in the fstab then we're not a system-as-root device, and in
// this case, we mount system first then pivot to it.  From that point on,
// we are effectively identical to a system-as-root device.
bool FirstStageMount::TrySwitchSystemAsRoot() {
    UseDsuIfPresent();
    // Preloading all AVB keys from the ramdisk before switching root to /system.
    PreloadAvbKeys();

    auto system_partition = std::find_if(fstab_.begin(), fstab_.end(), [](const auto& entry) {
        return entry.mount_point == "/system";
    });

    if (system_partition == fstab_.end()) return true;

    if (use_snapuserd_) {
        SaveRamdiskPathToSnapuserd();
    }
    // 调用挂载的 mount 函数
    if (MountPartition(system_partition, false /* erase_same_mounts */)) {
        if (dsu_not_on_userdata_ && fs_mgr_verity_is_check_at_most_once(*system_partition)) {
            LOG(ERROR) << "check_most_at_once forbidden on external media";
            return false;
        }

        SwitchRoot("/system");

    } else {
        PLOG(ERROR) << "Failed to mount /system";
        return false;
    }

    return true;
}

// 挂载一个节点
bool FirstStageMount::MountPartition(const Fstab::iterator& begin, bool erase_same_mounts,
                                     Fstab::iterator* end) {
    // Sets end to begin + 1, so we can just return on failure below.
    if (end) {
        *end = begin + 1;
    }
    // 创建挂载目录
    if (!fs_mgr_create_canonical_mount_point(begin->mount_point)) {
        return false;
    }

    if (begin->fs_mgr_flags.logical) {
        // 逻辑分区则需要修改一下挂载的 blk_device 名字
        // 因为 fstab 指定的是 system, 现在需要修改为 dm-6
        if (!fs_mgr_update_logical_partition(&(*begin))) {
            return false;
        }
        if (!block_dev_init_.InitDmDevice(begin->blk_device)) {
            return false;
        }
    }
    // 根据 metadata 中的相关信息设者 dm-verity 节点
    // 以上面的 system 分区为例:
    // 第一步 通过上面的 CreateLogicalPartition() 函数映射了 super 分区中 dm-2 为 system 分区
    // 在这里再次映射 dm-2 为 dm-6, dm-6 即为最终的挂载 blk_device
    // 至于内部流程后续在 device-mapper 原理再另起新篇幅介绍。
    if (!SetUpDmVerity(&(*begin))) {
        PLOG(ERROR) << "Failed to setup verity for '" << begin->mount_point << "'";
        return false;
    }

    // 这里调用最终的 mount 函数
    bool mounted = (fs_mgr_do_mount_one(*begin) == 0);

    // Try other mounts with the same mount point.
    Fstab::iterator current = begin + 1;
    for (; current != fstab_.end() && current->mount_point == begin->mount_point; current++) {
        if (!mounted) {
            // blk_device is already updated to /dev/dm-<N> by SetUpDmVerity() above.
            // Copy it from the begin iterator.
            current->blk_device = begin->blk_device;
            mounted = (fs_mgr_do_mount_one(*current) == 0);
        }
    }
    if (erase_same_mounts) {
        current = fstab_.erase(begin, current);
    }
    if (end) {
        *end = current;
    }
    return mounted;
}

// Android-13/system/core/fs_mgr/fs_mgr.cpp

// wrapper to __mount() and expects a fully prepared fstab_rec,
// unlike fs_mgr_do_mount which does more things with avb / verity etc.
int fs_mgr_do_mount_one(const FstabEntry& entry, const std::string& alt_mount_point) {
    // First check the filesystem if requested.
    if (entry.fs_mgr_flags.wait && !WaitForFile(entry.blk_device, 20s)) {
        LERROR << "Skipping mounting '" << entry.blk_device << "'";
    }

    auto& mount_point = alt_mount_point.empty() ? entry.mount_point : alt_mount_point;

    // Run fsck if needed
    prepare_fs_for_mount(entry.blk_device, entry, mount_point);

    int ret = __mount(entry.blk_device, mount_point, entry);
    if (ret) {
      ret = (errno == EBUSY) ? FS_MGR_DOMNT_BUSY : FS_MGR_DOMNT_FAILED;
    }

    return ret;
}

从下面的 LOG 可以看出,最终的挂载点为 /dev/block/dm-6,而建立 dm-2 和 dm-6 的映射则是 SetUpDmVerity() 中下面的信息定义
[libfs_avb]Built verity table: '1 /dev/block/dm-2 /dev/block/dm-2 4096 4096 398815 398815 sha256 5c78d69ea0b3cfd1af20c8d793afa1509acbe823e1674a06133aceeeeb46f184 1a57eb751

在这里插入图片描述

最终挂载完就是下面这个样子。
从 super 分区映射出 dm-2 设备称为 dm-linear
再从 dm-2 映射到 dm-6 设备称为 dm-verify,这个都基于 device-mapper ,后续介绍 device-mapper 中再细聊天。

在这里插入图片描述

四、super 分区编译过程

1.从 buidl/core/Makefile 开始

# If BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT is set, super.img is built from images in the
# $(PRODUCT_OUT) directory, and is built to $(PRODUCT_OUT)/super.img. Also, it will
# be built for non-dist builds. This is useful for devices that uses super.img directly, e.g.
# virtual devices.
ifeq (true,$(BOARD_BUILD_SUPER_IMAGE_BY_DEFAULT))
$(INSTALLED_SUPERIMAGE_TARGET): $(INSTALLED_SUPERIMAGE_DEPENDENCIES)
	$(call pretty,"Target super fs image for debug: $@")
	$(call build-superimage-target,$(INSTALLED_SUPERIMAGE_TARGET),\
          $(call intermediates-dir-for,PACKAGING,superimage_debug)/misc_info.txt)


# Build super.img by using $(INSTALLED_*IMAGE_TARGET) to $(1)
# $(1): built image path
# $(2): misc_info.txt path; its contents should match expectation of build_super_image.py
define build-superimage-target
  mkdir -p $(dir $(2))
  rm -rf $(2)
  $(call dump-super-image-info,$(2))
  $(foreach p,$(BOARD_SUPER_PARTITION_PARTITION_LIST), \
    echo "$(p)_image=$(INSTALLED_$(call to-upper,$(p))IMAGE_TARGET)" >> $(2);)
  $(if $(BUILDING_SYSTEM_OTHER_IMAGE), $(if $(filter system,$(BOARD_SUPER_PARTITION_PARTITION_LIST)), \
    echo "system_other_image=$(INSTALLED_SYSTEMOTHERIMAGE_TARGET)" >> $(2);))
  mkdir -p $(dir $(1))
  PATH=$(dir $(LPMAKE)):$$PATH \
    $(BUILD_SUPER_IMAGE) -v $(2) $(1)
endef

  1. 经过上面的层层调用后,最终调用到此应用打包 super.img $(BUILD_SUPER_IMAGE) -v $(2) $(1) ,这个命令最终为

out/host/linux-x86/bin/build_super_image -v \ out/target/product/xxx/obj/PACKAGING/target_files_intermediates/misc_info.txt \ out/target/product/xxx/super.img

备注:super.img 只是将 system/vendor 等 image 打包在一起,所以 super.img 需要依赖于 system.img、vendor.img、product.img 等这些镜像。

2. build_super_image.py

上一步调用到的 build_super_image 代码在 /Android-13/build/make/tools/releasetools/build_super_image.py 中,


def BuildSuperImageFromDict(info_dict, output):

  cmd = [info_dict["lpmake"],
         "--metadata-size", "65536",
         "--super-name", info_dict["super_metadata_device"]]

  ab_update = info_dict.get("ab_update") == "true"
  virtual_ab = info_dict.get("virtual_ab") == "true"
  virtual_ab_retrofit = info_dict.get("virtual_ab_retrofit") == "true"
  retrofit = info_dict.get("dynamic_partition_retrofit") == "true"
  block_devices = shlex.split(info_dict.get("super_block_devices", "").strip())
  groups = shlex.split(info_dict.get("super_partition_groups", "").strip())

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

  if ab_update and retrofit:
    cmd.append("--auto-slot-suffixing")
  if virtual_ab and not virtual_ab_retrofit:
    cmd.append("--virtual-ab")

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

  append_suffix = ab_update and not retrofit
  has_image = False
  for group in groups:
    group_size = info_dict["super_{}_group_size".format(group)]
    if append_suffix:
      cmd += ["--group", "{}_a:{}".format(group, group_size),
              "--group", "{}_b:{}".format(group, group_size)]
    else:
      cmd += ["--group", "{}:{}".format(group, group_size)]

    partition_list = shlex.split(
        info_dict["super_{}_partition_list".format(group)].strip())

    for partition in partition_list:
      image = info_dict.get("{}_image".format(partition))
      if image:
        has_image = True

      if not append_suffix:
        cmd += GetArgumentsForImage(partition, group, image)
        continue

      # For A/B devices, super partition always contains sub-partitions in
      # the _a slot, because this image should only be used for
      # bootstrapping / initializing the device. When flashing the image,
      # bootloader fastboot should always mark _a slot as bootable.
      cmd += GetArgumentsForImage(partition + "_a", group + "_a", image)

      other_image = None
      if partition == "system" and "system_other_image" in info_dict:
        other_image = info_dict["system_other_image"]
        has_image = True

      cmd += GetArgumentsForImage(partition + "_b", group + "_b", other_image)

  if info_dict.get("build_non_sparse_super_partition") != "true":
    cmd.append("--sparse")

  cmd += ["--output", output]

  common.RunAndCheckOutput(cmd)

  if retrofit and has_image:
    logger.info("Done writing images to directory %s", output)
  else:
    logger.info("Done writing image %s", output)

  return True

上面的 python 函数通过 misc_info.txt 设置一些参数,最后调用 lpmake 应用,调用 lpmake 参数如下
可以看到,参数中有 10 个 --partition 项,和第一节 lpdump 出来的 partition 内容一致。

lpmake --metadata-size 65536
	--super-name super
	--metadata-slots 3
	--virtual-ab
	--device super:9663676416 
	--group main_a:9661579264
	--group main_b:9661579264
	--partition odm_dlkm_a:readonly:348160:main_a --image odm_dlkm_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/odm_dlkm.img
	--partition odm_dlkm_b:readonly:0:main_b
	--partition product_a:readonly:2363891712:main_a --image product_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/product.img
	--partition product_b:readonly:0:main_b
	--partition system_a:readonly:1650864128:main_a --image system_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/system.img
	--partition system_b:readonly:143355904:main_b --image system_b=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/system_other.img
	--partition vendor_a:readonly:799784960:main_a --image vendor_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/vendor.img
	--partition vendor_b:readonly:0:main_b
	--partition vendor_dlkm_a:readonly:44457984:main_a --image vendor_dlkm_a=out/target/product/xxxxx/merged/temp/targetfiles-kv2fxhj8/IMAGES/vendor_dlkm.img
	--partition vendor_dlkm_b:readonly:0:main_b
	--sparse
	--output out/target/product/xxxxx/super.img


3. 最后的 lpmake

lpmake 的代码位于 /Android-13/system/extras/partition_tools/lpmake.cc


// /Android-13/system/core/fs_mgr/liblp/images.cpp

bool ImageBuilder::Build() {
    if (sparse_file_add_fill(device_images_[0].get(), 0, LP_PARTITION_RESERVED_BYTES, 0) < 0) {
        LERROR << "Could not add initial sparse block for reserved zeroes";
        return false;
    }

    std::string geometry_blob = SerializeGeometry(geometry_);
    std::string metadata_blob = SerializeMetadata(metadata_);
    metadata_blob.resize(geometry_.metadata_max_size);

    // Two copies of geometry, then two copies of each metadata slot.
    all_metadata_ += geometry_blob + geometry_blob;
    for (size_t i = 0; i < geometry_.metadata_slot_count * 2; i++) {
        all_metadata_ += metadata_blob;
    }
    // 上面就是 super.img meta 数据的生成过
    // 头部由连续的 2 个 geometry,后面跟随 6 个 meta 组成(非 A/B 分区就是 3 个。
    // 具体看下面的 dump 数据
    
    ///
}

super.img 整体结构如以及关键字段定义如下图:

在这里插入图片描述

实际 dump super.img 所看到的布局。
在这里插入图片描述

各个字段的定义在下面的代码中都有详细阐述。

/* Default name of the physical partition that holds logical partition entries.
 * The layout of this partition will look like:
 *
 *     +--------------------+
 *     | Disk Geometry      |
 *     +--------------------+
 *     | Geometry Backup    |
 *     +--------------------+
 *     | Metadata           |
 *     +--------------------+
 *     | Backup Metadata    |
 *     +--------------------+
 *     | Logical Partitions |
 *     +--------------------+
 */
 
 /* This structure is stored at block 0 in the first 4096 bytes of the
 * partition, and again in the following block. It is never modified and
 * describes how logical partition information can be located.
 */
typedef struct LpMetadataGeometry {
    /*  0: Magic signature (LP_METADATA_GEOMETRY_MAGIC). */
    uint32_t magic;

    /*  4: Size of the LpMetadataGeometry struct. */
    uint32_t struct_size;

    /*  8: SHA256 checksum of this struct, with this field set to 0. */
    uint8_t checksum[32];

    /* 40: Maximum amount of space a single copy of the metadata can use. This
     * must be a multiple of LP_SECTOR_SIZE.
     */
    uint32_t metadata_max_size;

    /* 44: Number of copies of the metadata to keep. For A/B devices, this
     * will be 2. For an A/B/C device, it would be 3, et cetera. For Non-A/B
     * it will be 1. A backup copy of each slot is kept, so if this is "2",
     * there will be four copies total.
     */
    uint32_t metadata_slot_count;

    /* 48: Logical block size. This is the minimal alignment for partition and
     * extent sizes, and it must be a multiple of LP_SECTOR_SIZE. Note that
     * this must be equal across all LUNs that comprise the super partition,
     * and thus this field is stored in the geometry, not per-device.
     */
    uint32_t logical_block_size;
} __attribute__((packed)) LpMetadataGeometry;

/* The logical partition metadata has a number of tables; they are described
 * in the header via the following structure.
 *
 * The size of the table can be computed by multiplying entry_size by
 * num_entries, and the result must not overflow a 32-bit signed integer.
 */
typedef struct LpMetadataTableDescriptor {
    /*  0: Location of the table, relative to end of the metadata header. */
    uint32_t offset;
    /*  4: Number of entries in the table. */
    uint32_t num_entries;
    /*  8: Size of each entry in the table, in bytes. */
    uint32_t entry_size;
} __attribute__((packed)) LpMetadataTableDescriptor;

/* Binary format for the header of the logical partition metadata format.
 *
 * The format has three sections. The header must occur first, and the
 * proceeding tables may be placed in any order after.
 *
 *  +-----------------------------------------+
 *  | Header data - fixed size                |
 *  +-----------------------------------------+
 *  | Partition table - variable size         |
 *  +-----------------------------------------+
 *  | Partition table extents - variable size |
 *  +-----------------------------------------+
 *
 * The "Header" portion is described by LpMetadataHeader. It will always
 * precede the other three blocks.
 *
 * All fields are stored in little-endian byte order when serialized.
 *
 * This struct is versioned; see the |major_version| and |minor_version|
 * fields.
 */
typedef struct LpMetadataHeader {
    /*  0: Four bytes equal to LP_METADATA_HEADER_MAGIC. */
    uint32_t magic;

    /*  4: Version number required to read this metadata. If the version is not
     * equal to the library version, the metadata should be considered
     * incompatible.
     */
    uint16_t major_version;

    /*  6: Minor version. A library supporting newer features should be able to
     * read metadata with an older minor version. However, an older library
     * should not support reading metadata if its minor version is higher.
     */
    uint16_t minor_version;

    /*  8: The size of this header struct. */
    uint32_t header_size;

    /* 12: SHA256 checksum of the header, up to |header_size| bytes, computed as
     * if this field were set to 0.
     */
    uint8_t header_checksum[32];

    /* 44: The total size of all tables. This size is contiguous; tables may not
     * have gaps in between, and they immediately follow the header.
     */
    uint32_t tables_size;

    /* 48: SHA256 checksum of all table contents. */
    uint8_t tables_checksum[32];

    /* 80: Partition table descriptor. */
    LpMetadataTableDescriptor partitions;
    /* 92: Extent table descriptor. */
    LpMetadataTableDescriptor extents;
    /* 104: Updateable group descriptor. */
    LpMetadataTableDescriptor groups;
    /* 116: Block device table. */
    LpMetadataTableDescriptor block_devices;

    /* Everything past here is header version 1.2+, and is only included if
     * needed. When liblp supporting >= 1.2 reads a < 1.2 header, it must
     * zero these additional fields.
     */

    /* 128: See LP_HEADER_FLAG_ constants for possible values. Header flags are
     * independent of the version number and intended to be informational only.
     * New flags can be added without bumping the version.
     */
    uint32_t flags;

    /* 132: Reserved (zero), pad to 256 bytes. */
    uint8_t reserved[124];
} __attribute__((packed)) LpMetadataHeader;


/* This struct defines a logical partition entry, similar to what would be
 * present in a GUID Partition Table.
 */
typedef struct LpMetadataPartition {
    /*  0: Name of this partition in ASCII characters. Any unused characters in
     * the buffer must be set to 0. Characters may only be alphanumeric or _.
     * The name must include at least one ASCII character, and it must be unique
     * across all partition names. The length (36) is the same as the maximum
     * length of a GPT partition name.
     */
    char name[36];

    /* 36: Attributes for the partition (see LP_PARTITION_ATTR_* flags above). */
    uint32_t attributes;

    /* 40: Index of the first extent owned by this partition. The extent will
     * start at logical sector 0. Gaps between extents are not allowed.
     */
    uint32_t first_extent_index;

    /* 44: Number of extents in the partition. Every partition must have at
     * least one extent.
     */
    uint32_t num_extents;

    /* 48: Group this partition belongs to. */
    uint32_t group_index;
} __attribute__((packed)) LpMetadataPartition;


  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android系统开发是指使用Android平台进行应用程序开发的过程。在Android系统开发中,开发者可以利用Android提供的开发工具和框架来创建移动应用程序,这些应用程序可以运行在Android设备上。 Android系统开发主要包括以下几个方面: 1. 硬件要求:Android系统开发需要开发者了解Android设备的硬件要求和限制,以便在应用程序开发过程中能够充分利用设备的功能和性能。 2. 开发环境搭建:开发者需要安装Android开发工具包(Android SDK),其中包括Android Studio集成开发环境(IDE),以及相关的开发工具和库。通过Android Studio,开发者可以创建、调试和测试Android应用程序。 3. 应用程序结构:Android应用程序采用一种基于组件的架构模式。开发者可以通过定义活动(Activity)、服务(Service)、广播接收器(Broadcast Receiver)和内容提供器(Content Provider)等组件来构建应用程序。 4. 用户界面设计:Android系统提供了丰富的用户界面组件,开发者可以使用这些组件来创建各种交互界面,包括布局、视图和对话框等。 5. 数据存储和管理:Android系统提供了多种数据存储和管理方式,开发者可以选择适合应用程序需求的存储方式,包括数据库(SQLite)、文件系统和SharedPreferences等。 6. 资源管理:Android系统支持多种类型的资源管理,包括图像、音频、字符串和布局等。开发者可以将这些资源文件添加到应用程序中,并在代码中引用和使用。 7. 应用程序发布:完成应用程序开发后,开发者可以利用Android开发工具包提供的工具和服务将应用程序打包、签名并发布到Google Play商店或其他应用分发平台,供用户下载和安装。 总结起来,Android系统开发是一项涉及多个方面的复杂任务,需要开发者具备一定的编程和移动应用开发经验,并熟悉Android平台的开发工具和框架。通过合理利用Android系统提供的功能和资源,开发者可以创建出功能强大、用户友好的Android应用程序。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值