摘要:本节主要来进行Android10.0 Image打包流程,理解system.img是如何打包的

阅读本文大约需要花费28分钟。

文章首发微信公众号:IngresGe

专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢!

欢迎关注我的公众号!

[Android取经之路] 的源码都基于Android-Q(10.0) 进行分析

[Android取经之路] 系列文章:

《系统启动篇》

  1. Android系统架构
  2. Android是怎么启动的
  3. Android 10.0系统启动之init进程
  4. Android10.0系统启动之Zygote进程
  5. Android 10.0 系统启动之SystemServer进程
  6. Android 10.0 系统服务之ActivityMnagerService
  7. Android10.0系统启动之Launcher(桌面)启动流程
  8. Android10.0应用进程创建过程以及Zygote的fork流程
  9. Android 10.0 PackageManagerService(一)工作原理及启动流程
  10. Android 10.0 PackageManagerService(二)权限扫描
  11. Android 10.0 PackageManagerService(三)APK扫描
  12. Android 10.0 PackageManagerService(四)APK安装流程

《日志系统篇》

  1. Android10.0 日志系统分析(一)-logd、logcat 指令说明、分类和属性
  2. Android10.0 日志系统分析(二)-logd、logcat架构分析及日志系统初始化
  3. Android10.0 日志系统分析(三)-logd、logcat读写日志源码分析
  4. Android10.0 日志系统分析(四)-selinux、kernel日志在logd中的实现​

《Binder通信原理》

  1. Android10.0 Binder通信原理(一)Binder、HwBinder、VndBinder概要
  2. Android10.0 Binder通信原理(二)-Binder入门篇
  3. Android10.0 Binder通信原理(三)-ServiceManager篇
  4. Android10.0 Binder通信原理(四)-Native-C\C++实例分析
  5. Android10.0 Binder通信原理(五)-Binder驱动分析
  6. Android10.0 Binder通信原理(六)-Binder数据如何完成定向打击
  7. Android10.0 Binder通信原理(七)-Framework binder示例
  8. Android10.0 Binder通信原理(八)-Framework层分析
  9. Android10.0 Binder通信原理(九)-AIDL Binder示例
  10. Android10.0 Binder通信原理(十)-AIDL原理分析-Proxy-Stub设计模式
  11. Android10.0 Binder通信原理(十一)-Binder总结

  《HwBinder通信原理》

  1. HwBinder入门篇-Android10.0 HwBinder通信原理(一)
  2. HIDL详解-Android10.0 HwBinder通信原理(二)
  3. HIDL示例-C++服务创建Client验证-Android10.0 HwBinder通信原理(三)
  4. HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)
  5. HwServiceManager篇-Android10.0 HwBinder通信原理(五)
  6. Native层HIDL服务的注册原理-Android10.0 HwBinder通信原理(六)
  7. Native层HIDL服务的获取原理-Android10.0 HwBinder通信原理(七)
  8. JAVA层HIDL服务的注册原理-Android10.0 HwBinder通信原理(八)
  9. JAVA层HIDL服务的获取原理-Android10.0 HwBinder通信原理(九)
  10. HwBinder驱动篇-Android10.0 HwBinder通信原理(十)
  11. HwBinder原理总结-Android10.0 HwBinder通信原理(十一)

《编译原理》

  1. 编译系统入门篇-Android10.0编译系统(一)
  2. 编译环境初始化-Android10.0编译系统(二)
  3. make编译过程-Android10.0编译系统(三)
  4. Image打包流程-Android10.0编译系统(四)
  5. Kati详解-Android10.0编译系统(五)
  6. Blueprint简介-Android10.0编译系统(六)
  7. Blueprint代码详细分析-Android10.0编译系统(七)

1 概述

前面我们讲完了Android10.0 编译的初始化和make的完整流程,从make中我们看到了,最终编译会生成system.img、super.img、ramdisk.img等镜像文件,我们把这些镜像文件烧录到手机中,即可完成版本的替换升级。

这一节我们来一起看看这些image是如何打包生成的

2 image打包入口

在上一节的main.mk中,最后两步定义了需要编译的image和构建一个rom的过程.

 image构建和打包的一些依赖关系如下图所示:

[build/make/core/main.mk]...
.PHONY: ramdisk
ramdisk: $(INSTALLED_RAMDISK_TARGET).PHONY: userdataimage
userdataimage: $(INSTALLED_USERDATAIMAGE_TARGET)
DATATARBALL_TARGET).PHONY: cacheimage
cacheimage: $(INSTALLED_CACHEIMAGE_TARGET).PHONY: odmimage
odmimage: $(INSTALLED_ODMIMAGE_TARGET).PHONY: systemotherimage
systemotherimage: $(INSTALLED_SYSTEMOTHERIMAGE_TARGET).PHONY: superimage_empty
superimage_empty: $(INSTALLED_SUPERIMAGE_EMPTY_TARGET).PHONY: bootimage
bootimage: $(INSTALLED_BOOTIMAGE_TARGET).PHONY: bootimage_debug
bootimage_debug: $(INSTALLED_DEBUG_BOOTIMAGE_TARGET)....PHONY: droidcore
droidcore: $(filter $(HOST_OUT_ROOT)/%,$(modules_to_install)) \$(INSTALLED_SYSTEMIMAGE_TARGET) \$(INSTALLED_RAMDISK_TARGET) \$(INSTALLED_BOOTIMAGE_TARGET) \$(INSTALLED_DEBUG_RAMDISK_TARGET) \$(INSTALLED_DEBUG_BOOTIMAGE_TARGET) \$(INSTALLED_RECOVERYIMAGE_TARGET) \$(INSTALLED_VBMETAIMAGE_TARGET) \$(INSTALLED_USERDATAIMAGE_TARGET) \$(INSTALLED_CACHEIMAGE_TARGET) \$(INSTALLED_BPTIMAGE_TARGET) \$(INSTALLED_VENDORIMAGE_TARGET) \$(INSTALLED_ODMIMAGE_TARGET) \$(INSTALLED_SUPERIMAGE_EMPTY_TARGET) \...\auxiliary \soong_docs
...

main.mk中只是做了一些定义和启动编译流程,正在的image打包在build/core/Makefile中完成

[build/make/core/main.mk]
ifdef FULL_BUILD...# TODO: Remove the 3 places in the tree that use ALL_DEFAULT_INSTALLED_MODULES
# and get rid of it from this list.
modules_to_install := $(sort \$(ALL_DEFAULT_INSTALLED_MODULES) \$(product_target_FILES) \$(product_host_FILES) \$(call get-tagged-modules,$(tags_to_install)) \$(CUSTOM_MODULES) \)
...
# build/make/core/Makefile contains extra stuff that we don't want to pollute this
# top-level makefile with.  It expects that ALL_DEFAULT_INSTALLED_MODULES
# contains everything that's built during the current make, but it also further
# extends ALL_DEFAULT_INSTALLED_MODULES.
ALL_DEFAULT_INSTALLED_MODULES := $(modules_to_install)
include $(BUILD_SYSTEM)/Makefile
modules_to_install := $(sort $(ALL_DEFAULT_INSTALLED_MODULES))
ALL_DEFAULT_INSTALLED_MODULES :=...
#endif

在build/core/Makefile中定义了很多image的生成规则,例如:system.img,boot.img,recovery.img,vendor.img,super.img,下面我们就以system.img为例,详细的来看看image的具体打包细节。

[build/core/Makefile]
...
.PHONY: systemimage
.PHONY: event-log-tags
.PHONY: ramdisk-nodeps
.PHONY: bootimage-nodeps
.PHONY: bootimage-nodeps
.PHONY: bootimage-nodeps
.PHONY: bootimage-nodeps
.PHONY: notice_files
.PHONY: otacerts
.PHONY: recoveryimage-nodeps
.PHONY: recoveryimage
.PHONY: ramdisk_debug-nodeps
.PHONY: bootimage_debug-nodeps
.PHONY: installed-file-list
.PHONY: systemimage-nodeps snod
.PHONY: sync syncsys
.PHONY: systemtarball-nodeps
.PHONY: stnod
.PHONY: platform
.PHONY: platform-java
.PHONY: boottarball-nodeps btnod
.PHONY: userdataimage-nodeps
.PHONY: userdatatarball-nodeps
.PHONY: bptimage-nodeps
.PHONY: cacheimage-nodeps
.PHONY: systemotherimage-nodeps
.PHONY: vendorimage-nodeps vnod
.PHONY: productimage-nodeps pnod
.PHONY: productservicesimage-nodeps psnod
.PHONY: odmimage-nodeps onod
.PHONY: vbmetaimage-nodeps
.PHONY: otatools
.PHONY: otatools-package
.PHONY: target-files-package
.PHONY: otapackage
.PHONY: otardppackage
.PHONY: superimage_dist
.PHONY: superimage
.PHONY: superimage-nodeps supernod
...

3.systemimage 打包

system.img打包的是system分区中的文件,相关打包内容如下:

# Rules that need to be present for the all targets, even
# if they don't do anything.
.PHONY: systemimage
systemimage:...
INSTALLED_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/system.img
SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)$(INSTALLED_SYSTEMIMAGE_TARGET): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH)@echo "Install system fs image: $@"$(copy-file-to-target)$(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))systemimage: $(INSTALLED_SYSTEMIMAGE_TARGET).PHONY: systemimage-nodeps snod
systemimage-nodeps snod: $(filter-out systemimage-nodeps snod,$(MAKECMDGOALS)) \| $(INTERNAL_USERIMAGES_DEPS)@echo "make $@: ignoring dependencies"$(call build-systemimage-target,$(INSTALLED_SYSTEMIMAGE_TARGET))$(hide) $(call assert-max-image-size,$(INSTALLED_SYSTEMIMAGE_TARGET),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))
...

关于system.img,这里定义了两个伪目标systemimage 和 systemimage-nodeps。

systemimage表示在打包system.img之前,要根据依赖规则重新生成所有要进行打包的文件。

systemimage-nodeps则不需要根据依赖规则重新生成所有需要打包的文件而直接打包system.img文件。

systemimage 依赖于$(INSTALLED_SYSTEMIMAGE_TARGET)。

3.1 INSTALLED_SYSTEMIMAGE_TARGET

从上面的代码看到,systemimage 依赖于INSTALLED_SYSTEMIMAGE_TARGET,最终生成目标文件

$(PRODUCT_OUT)/system.img
$(INSTALLED_SYSTEMIMAGE_TARGET): $(BUILT_SYSTEMIMAGE) $(RECOVERY_FROM_BOOT_PATCH)@echo "Install system fs image: $@"$(copy-file-to-target)$(hide) $(call assert-max-image-size,$@ $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_SYSTEMIMAGE_PARTITION_SIZE))

其中,INSTALLED_SYSTEMIMAGE_TARGET依赖于BUILT_SYSTEMIMAGE和RECOVERY_FROM_BOOT_PATCH,再调用了函数copy-file-to-target进行文件拷贝。

3.1.1  copy-file-to-target

copy-file-to-target 在/build/make/core/definitions.mk 中被定义,主要用于是拷贝文件,并且在拷贝的过程中会保留文件的权限和覆盖已有的文件。

它会创建/out/target/product/xxx 目录, xxx表示产品的名称,然后把文件拷贝到该目录中

[/build/make/core/definitions.mk]
# Copy a single file from one place to another,
# preserving permissions and overwriting any existing
# file.
# When we used acp, it could not handle high resolution timestamps
# on file systems like ext4. Because of that, '-t' option was disabled
# and copy-file-to-target was identical to copy-file-to-new-target.
# Keep the behavior until we audit and ensure that switching this back
# won't break anything.
define copy-file-to-target
@mkdir -p $(dir $@)
$(hide) rm -f $@
$(hide) cp "$<" "$@"
endef

3.1.2 RECOVERY_FROM_BOOT_PATCH

RECOVERY_FROM_BOOT_PATCH 描述的是一个patch文件,依赖规则如下所示:

ifneq (,$(filter true, $(BOARD_BUILD_SYSTEM_ROOT_IMAGE) $(BOARD_INCLUDE_RECOVERY_DTBO) $(BOARD_INCLUDE_RECOVERY_ACPIO)))
diff_tool := $(HOST_OUT_EXECUTABLES)/bsdiff
else
diff_tool := $(HOST_OUT_EXECUTABLES)/imgdiff
endif
intermediates := $(call intermediates-dir-for,PACKAGING,recovery_patch)
RECOVERY_FROM_BOOT_PATCH := $(intermediates)/recovery_from_boot.p
$(RECOVERY_FROM_BOOT_PATCH): PRIVATE_DIFF_TOOL := $(diff_tool)
$(RECOVERY_FROM_BOOT_PATCH): \$(INSTALLED_RECOVERYIMAGE_TARGET) \$(INSTALLED_BOOTIMAGE_TARGET) \$(diff_tool)@echo "Construct recovery from boot"mkdir -p $(dir $@)$(PRIVATE_DIFF_TOOL) $(INSTALLED_BOOTIMAGE_TARGET) $(INSTALLED_RECOVERYIMAGE_TARGET) $@

RECOVERY_FROM_BOOT_PATCH 依赖的patch文件为:$(intermediates)/recovery_from_boot.p,表示的是recovery.img和boot.img之间的差异,存在于system分区中,可以通过boot.img和recovery_from_boot.p构造一个recovery.img。

3.1.3 BUILT_SYSTEMIMAGE

BUILT_SYSTEMIMAGE 最终会把system.img编译到 out/target/product/generic/obj/PACKAGING/systemimage_intermediates/system.img中。

BUILT_SYSTEMIMAGE 依赖于FULL_SYSTEMIMAGE_DEPS、INSTALLED_FILES_FILE和BUILD_IMAGE_SRCS,通过调用 函数build-systemimage-target 来编译systemimage,相关依赖如下所示:

systemimage_intermediates := \$(call intermediates-dir-for,PACKAGING,systemimage)
BUILT_SYSTEMIMAGE := $(systemimage_intermediates)/system.img$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE) $(BUILD_IMAGE_SRCS)$(call build-systemimage-target,$@)

BUILD_IMAGE_SRCS 在[/build/make/core/config.mk] 中定义,配置了build/make/tools/releasetools中的python脚本参与编译

BUILD_IMAGE_SRCS := $(wildcard build/make/tools/releasetools/*.py)

INSTALLED_FILES_FILE 依赖的是文件$(PRODUCT_OUT)/installed-files.txt,这是已安装的文件列表,这些文件要打包到system.img中,他也依赖于FULL_SYSTEMIMAGE_DEPS

 INSTALLED_FILES_FILE 的依赖描述如下所示:

# installed file list
# Depending on anything that $(BUILT_SYSTEMIMAGE) depends on.
# We put installed-files.txt ahead of image itself in the dependency graph
# so that we can get the size stat even if the build fails due to too large
# system image.
INSTALLED_FILES_FILE := $(PRODUCT_OUT)/installed-files.txt
INSTALLED_FILES_JSON := $(INSTALLED_FILES_FILE:.txt=.json)
$(INSTALLED_FILES_FILE): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON)
$(INSTALLED_FILES_FILE): $(FULL_SYSTEMIMAGE_DEPS) $(FILESLIST)@echo Installed file list: $@@mkdir -p $(dir $@)@rm -f $@$(hide) $(FILESLIST) $(TARGET_OUT) > $(@:.txt=.json)$(hide) build/make/tools/fileslist_util.py -c $(@:.txt=.json) > $@

FULL_SYSTEMIMAGE_DEPS依赖于INTERNAL_SYSTEMIMAGE_FILES 和INTERNAL_USERIMAGES_DEPS,列出了制作system.img所需要的工具和制作system.img所需要的文件。

INTERNAL_USERIMAGES_DEPS := $(SIMG2IMG)
INTERNAL_USERIMAGES_DEPS += $(MKEXTUSERIMG) $(MAKE_EXT4FS) $(E2FSCK) $(TUNE2FS)
ifeq ($(TARGET_USERIMAGES_USE_F2FS),true)
INTERNAL_USERIMAGES_DEPS += $(MKF2FSUSERIMG) $(MAKE_F2FS)
endif
...
INTERNAL_SYSTEMIMAGE_FILES := $(sort $(filter $(TARGET_OUT)/%, \$(ALL_GENERATED_SOURCES) \$(ALL_DEFAULT_INSTALLED_MODULES) \$(PDK_FUSION_SYSIMG_FILES) \$(RECOVERY_RESOURCE_ZIP)) \$(PDK_FUSION_SYMLINK_STAMP))FULL_SYSTEMIMAGE_DEPS := $(INTERNAL_SYSTEMIMAGE_FILES) $(INTERNAL_USERIMAGES_DEPS)

INTERNAL_USERIMAGES_DEPS:列出了制作system.img所需要的工具,例如out/host/linux-x86/bin/simg2img、out/host/linux-x86/bin/mkuserimg_mke2fs 等,如果支持f2fs的文件系统,会加载out/host/linux-x86/bin/make_f2fs

INTERNAL_SYSTEMIMAGE_FILES:列出了制作system.img所需要的文件,释义如下:

ALL_GENERATED_SOURCES:描述的是要拷贝到目标设备上去的由工具自动生成的源代码文件。

ALL_DEFAULT_INSTALLED_MODULES:描述的是所有需要安装的module

PDK_FUSION_SYSIMG_FILES:是从PDK(Platform Development Kit)提取出来的相关文件

RECOVERY_RESOURCE_ZIP:描述的是Android的recovery系统要使用的资源文件,对应于/system/etc目录下的recovery-resource.dat文件。

PDK_FUSION_SYMLINK_STAMP:PDK的符号链接文件

3.1.4 build-systemimage-target

BUILT_SYSTEMIMAGE 通过调用函数build-systemimage-target 来生成img。

首先创进行了一些vendor\product\product_service的link操作,然后创建一个out/target/product/generic/obj/PACKAGING/systemimage_intermediates/ 目录,并先删除system_image_info.txt文件,接着调用generate-image-prop-dictionary,生成system.img的信息,保存到system_image_info.txt中。

最后调用build/make/tools/releasetools/build_image.py来生成system.img。

  build-systemimage-target的定义如下所示:

define build-systemimage-target@echo "Target system fs image: $(1)"$(call create-system-vendor-symlink)$(call create-system-product-symlink)$(call create-system-product_services-symlink)$(call check-apex-libs-absence-on-disk)@mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt$(call generate-image-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt,system, \skip_fsck=true)$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \build/make/tools/releasetools/build_image.py \$(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \|| ( mkdir -p $${DIST_DIR}; cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \exit 1 )
endef

3.1.4.1 create-system-vendor-symlink

如果存在vendor目录,就给vendor目录创建一个软连接。即/system/vendor 目录会被link到/vendor目录。

define create-system-vendor-symlink
$(hide) if [ -d $(TARGET_OUT)/vendor ] && [ ! -h $(TARGET_OUT)/vendor ]; then \echo 'Non-symlink $(TARGET_OUT)/vendor detected!' 1>&2; \echo 'You cannot install files to $(TARGET_OUT)/vendor while building a separate vendor.img!' 1>&2; \exit 1; \
fi

3.1.4.2 create-system-product-symlink

如果存在product目录,就给product目录创建一个软连接。即/system/product 目录会被link到/product目录。

define create-system-product-symlink
$(hide) if [ -d $(TARGET_OUT)/product ] && [ ! -h $(TARGET_OUT)/product ]; then \echo 'Non-symlink $(TARGET_OUT)/product detected!' 1>&2; \echo 'You cannot install files to $(TARGET_OUT)/product while building a separate product.img!' 1>&2; \exit 1; \
fi

3.1.4.3 create-system-product_services-symlink

如果存在product_services目录,就给product_services目录创建一个软连接。即/system/product_services 目录会被link到/product_services目录。

define create-system-product_services-symlink
$(hide) if [ -d $(TARGET_OUT)/product_services ] && [ ! -h $(TARGET_OUT)/product_services ]; then \echo 'Non-symlink $(TARGET_OUT)/product_services detected!' 1>&2; \echo 'You cannot install files to $(TARGET_OUT)/product_services while building a separate product_services.img!' 1>&2; \exit 1; \
fi

3.2 build_image.py编译system.img

 $(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \build/make/tools/releasetools/build_image.py \$(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT) \|| ( mkdir -p $${DIST_DIR}; cp $(INSTALLED_FILES_FILE) $${DIST_DIR}/installed-files-rescued.txt; \exit 1 )

执行build_image.py时,传入了4个参数:

$(TARGET_OUT) $(systemimage_intermediates)/system_image_info.txt $(1) $(TARGET_OUT)

$(TARGET_OUT)  :对应目录out/target/product/generic/system

$(systemimage_intermediates)/system_image_info.txt :system.img的配置文件

3.2.1 main()

首先进行参数检查,如果参数个数小于4,直接退出,如果要生成system.img,mount指向system,调用ImagePropFromGlobalDict()来获取image的参数,再调用BuildImage()进行image的编译。

def main(argv):if len(argv) != 4:print(__doc__)sys.exit(1)common.InitLogging()in_dir = argv[0]glob_dict_file = argv[1]out_file = argv[2]target_out = argv[3]glob_dict = LoadGlobalDict(glob_dict_file)if "mount_point" in glob_dict:# The caller knows the mount point and provides a dictionary needed by# BuildImage().image_properties = glob_dictelse:image_filename = os.path.basename(out_file)mount_point = ""if image_filename == "system.img":mount_point = "system"elif image_filename == "system_other.img":mount_point = "system_other"elif image_filename == "userdata.img":mount_point = "data"elif image_filename == "cache.img":mount_point = "cache"elif image_filename == "vendor.img":mount_point = "vendor"elif image_filename == "odm.img":mount_point = "odm"elif image_filename == "oem.img":mount_point = "oem"elif image_filename == "product.img":mount_point = "product"elif image_filename == "product_services.img":mount_point = "product_services"else:logger.error("Unknown image file name %s", image_filename)sys.exit(1)image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)try:BuildImage(in_dir, image_properties, out_file, target_out)except:logger.error("Failed to build %s from %s", out_file, in_dir)raise

3.2.2 ImagePropFromGlobalDict()

传入的mount_point为system,把类似system_reserved_size这些参数,存入system_image_info.txt中。

def ImagePropFromGlobalDict(glob_dict, mount_point):"""Build an image property dictionary from the global dictionary.Args:glob_dict: the global dictionary from the build system.mount_point: such as "system", "data" etc."""d = {}if "build.prop" in glob_dict:bp = glob_dict["build.prop"]if "ro.build.date.utc" in bp:d["timestamp"] = bp["ro.build.date.utc"]def copy_prop(src_p, dest_p):"""Copy a property from the global dictionary.Args:src_p: The source property in the global dictionary.dest_p: The destination property.Returns:True if property was found and copied, False otherwise."""if src_p in glob_dict:d[dest_p] = str(glob_dict[src_p])return Truereturn Falsecommon_props = ("extfs_sparse_flag","squashfs_sparse_flag","selinux_fc","skip_fsck","ext_mkuserimg","verity","verity_key","verity_signer_cmd","verity_fec","verity_disable","avb_enable","avb_avbtool","avb_salt","use_dynamic_partition_size",)for p in common_props:copy_prop(p, p)d["mount_point"] = mount_pointif mount_point == "system":copy_prop("avb_system_hashtree_enable", "avb_hashtree_enable")copy_prop("avb_system_add_hashtree_footer_args","avb_add_hashtree_footer_args")copy_prop("avb_system_key_path", "avb_key_path")copy_prop("avb_system_algorithm", "avb_algorithm")copy_prop("fs_type", "fs_type")# Copy the generic system fs type first, override with specific one if# available.copy_prop("system_fs_type", "fs_type")copy_prop("system_headroom", "partition_headroom")copy_prop("system_size", "partition_size")if not copy_prop("system_journal_size", "journal_size"):d["journal_size"] = "0"copy_prop("system_verity_block_device", "verity_block_device")copy_prop("system_root_image", "system_root_image")copy_prop("root_dir", "root_dir")copy_prop("root_fs_config", "root_fs_config")copy_prop("ext4_share_dup_blocks", "ext4_share_dup_blocks")copy_prop("system_squashfs_compressor", "squashfs_compressor")copy_prop("system_squashfs_compressor_opt", "squashfs_compressor_opt")copy_prop("system_squashfs_block_size", "squashfs_block_size")copy_prop("system_squashfs_disable_4k_align", "squashfs_disable_4k_align")copy_prop("system_base_fs_file", "base_fs_file")copy_prop("system_extfs_inode_count", "extfs_inode_count")if not copy_prop("system_extfs_rsv_pct", "extfs_rsv_pct"):d["extfs_rsv_pct"] = "0"copy_prop("system_reserved_size", "partition_reserved_size")elif mount_point == "system_other":...elif mount_point == "data":...elif mount_point == "cache":...elif mount_point == "vendor":...elif mount_point == "product":...elif mount_point == "product_services":...elif mount_point == "odm":...elif mount_point == "oem":...d["partition_name"] = mount_pointreturn d

3.2.3 BuildImage()

上面得到system.img的参数后,接下来执行image的编译。

def BuildImage(in_dir, prop_dict, out_file, target_out=None):"""Builds an image for the files under in_dir and writes it to out_file.Args:in_dir: Path to input directory.prop_dict: A property dict that contains info like partition size. Valueswill be updated with computed values.out_file: The output image file.target_out: Path to the TARGET_OUT directory as in Makefile. It actuallypoints to the /system directory under PRODUCT_OUT. fs_config (the oneunder system/core/libcutils) reads device specific FS config files fromthere.Raises:BuildImageError: On build image failures."""in_dir, fs_config = SetUpInDirAndFsConfig(in_dir, prop_dict)build_command = []fs_type = prop_dict.get("fs_type", "")fs_spans_partition = Trueif fs_type.startswith("squash"):fs_spans_partition = False# Get a builder for creating an image that's to be verified by Verified Boot,# or None if not applicable.verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)if (prop_dict.get("use_dynamic_partition_size") == "true" and"partition_size" not in prop_dict):# If partition_size is not defined, use output of `du' + reserved_size.size = GetDiskUsage(in_dir)logger.info("The tree size of %s is %d MB.", in_dir, size // BYTES_IN_MB)# If not specified, give us 16MB margin for GetDiskUsage error ...reserved_size = int(prop_dict.get("partition_reserved_size", BYTES_IN_MB * 16))partition_headroom = int(prop_dict.get("partition_headroom", 0))if fs_type.startswith("ext4") and partition_headroom > reserved_size:reserved_size = partition_headroomsize += reserved_size# Round this up to a multiple of 4K so that avbtool workssize = common.RoundUpTo4K(size)if fs_type.startswith("ext"):prop_dict["partition_size"] = str(size)prop_dict["image_size"] = str(size)if "extfs_inode_count" not in prop_dict:prop_dict["extfs_inode_count"] = str(GetInodeUsage(in_dir))logger.info("First Pass based on estimates of %d MB and %s inodes.",size // BYTES_IN_MB, prop_dict["extfs_inode_count"])BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)sparse_image = Falseif "extfs_sparse_flag" in prop_dict:sparse_image = Truefs_dict = GetFilesystemCharacteristics(out_file, sparse_image)os.remove(out_file)block_size = int(fs_dict.get("Block size", "4096"))free_size = int(fs_dict.get("Free blocks", "0")) * block_sizereserved_size = int(prop_dict.get("partition_reserved_size", 0))partition_headroom = int(fs_dict.get("partition_headroom", 0))if fs_type.startswith("ext4") and partition_headroom > reserved_size:reserved_size = partition_headroomif free_size <= reserved_size:logger.info("Not worth reducing image %d <= %d.", free_size, reserved_size)else:size -= free_sizesize += reserved_sizeif reserved_size == 0:# add .3% marginsize = size * 1003 // 1000# Use a minimum size, otherwise we will fail to calculate an AVB footer# or fail to construct an ext4 image.size = max(size, 256 * 1024)if block_size <= 4096:size = common.RoundUpTo4K(size)else:size = ((size + block_size - 1) // block_size) * block_sizeextfs_inode_count = prop_dict["extfs_inode_count"]inodes = int(fs_dict.get("Inode count", extfs_inode_count))inodes -= int(fs_dict.get("Free inodes", "0"))# add .2% margin or 1 inode, whichever is greaterspare_inodes = inodes * 2 // 1000min_spare_inodes = 1if spare_inodes < min_spare_inodes:spare_inodes = min_spare_inodesinodes += spare_inodesprop_dict["extfs_inode_count"] = str(inodes)prop_dict["partition_size"] = str(size)logger.info("Allocating %d Inodes for %s.", inodes, out_file)if verity_image_builder:size = verity_image_builder.CalculateDynamicPartitionSize(size)prop_dict["partition_size"] = str(size)logger.info("Allocating %d MB for %s.", size // BYTES_IN_MB, out_file)prop_dict["image_size"] = prop_dict["partition_size"]# Adjust the image size to make room for the hashes if this is to be verified.if verity_image_builder:max_image_size = verity_image_builder.CalculateMaxImageSize()prop_dict["image_size"] = str(max_image_size)mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)# Check if there's enough headroom space available for ext4 image.if "partition_headroom" in prop_dict and fs_type.startswith("ext4"):CheckHeadroom(mkfs_output, prop_dict)if not fs_spans_partition and verity_image_builder:verity_image_builder.PadSparseImage(out_file)# Create the verified image if this is to be verified.if verity_image_builder:verity_image_builder.Build(out_file)

3.2.4 BuildImageMkfs()

Android10.0 AOSP的system_image_info.txt中,几个变量如下:

ext_mkuserimg=mkuserimg_mke2fs
fs_type=ext4

执行以下命令进行image的打包:

out/host/linux-x86/bin/mkuserimg_mke2fs '/out/soong/.temp/tmpK__WLx' 'out/target/product/generic/obj/PACKAGING/systemimage_intermediates/system.img' 'ext4' '/' '985493504' -j 0 -D 'out/target/product/generic/system' -L '/' -i 3280 -M 0 -c --inode_size 256 out/target/product/generic/obj/ETC/file_contexts.bin_intermediates/file_contexts.bin
def BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config):...build_command = []fs_type = prop_dict.get("fs_type", "")run_e2fsck = Falseif fs_type.startswith("ext"):build_command = [prop_dict["ext_mkuserimg"]]if "extfs_sparse_flag" in prop_dict:build_command.append(prop_dict["extfs_sparse_flag"])run_e2fsck = Truebuild_command.extend([in_dir, out_file, fs_type,prop_dict["mount_point"]])build_command.append(prop_dict["image_size"])if "journal_size" in prop_dict:build_command.extend(["-j", prop_dict["journal_size"]])if "timestamp" in prop_dict:build_command.extend(["-T", str(prop_dict["timestamp"])])if fs_config:build_command.extend(["-C", fs_config])if target_out:build_command.extend(["-D", target_out])if "block_list" in prop_dict:build_command.extend(["-B", prop_dict["block_list"]])if "base_fs_file" in prop_dict:base_fs_file = ConvertBlockMapToBaseFs(prop_dict["base_fs_file"])build_command.extend(["-d", base_fs_file])build_command.extend(["-L", prop_dict["mount_point"]])if "extfs_inode_count" in prop_dict:build_command.extend(["-i", prop_dict["extfs_inode_count"]])if "extfs_rsv_pct" in prop_dict:build_command.extend(["-M", prop_dict["extfs_rsv_pct"]])if "flash_erase_block_size" in prop_dict:build_command.extend(["-e", prop_dict["flash_erase_block_size"]])if "flash_logical_block_size" in prop_dict:build_command.extend(["-o", prop_dict["flash_logical_block_size"]])# Specify UUID and hash_seed if using mke2fs.if prop_dict["ext_mkuserimg"] == "mkuserimg_mke2fs":if "uuid" in prop_dict:build_command.extend(["-U", prop_dict["uuid"]])if "hash_seed" in prop_dict:build_command.extend(["-S", prop_dict["hash_seed"]])if "ext4_share_dup_blocks" in prop_dict:build_command.append("-c")build_command.extend(["--inode_size", "256"])if "selinux_fc" in prop_dict:build_command.append(prop_dict["selinux_fc"])elif fs_type.startswith("squash"):...elif fs_type.startswith("f2fs"):...else:raise BuildImageError("Error: unknown filesystem type: {}".format(fs_type))try:mkfs_output = common.RunAndCheckOutput(build_command)except:try:du = GetDiskUsage(in_dir)du_str = "{} bytes ({} MB)".format(du, du // BYTES_IN_MB)# Suppress any errors from GetDiskUsage() to avoid hiding the real errors# from common.RunAndCheckOutput().except Exception:  # pylint: disable=broad-exceptlogger.exception("Failed to compute disk usage with du")du_str = "unknown"...raiseif run_e2fsck and prop_dict.get("skip_fsck") != "true":unsparse_image = UnsparseImage(out_file, replace=False)# Run e2fsck on the inflated image filee2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]try:common.RunAndCheckOutput(e2fsck_command)finally:os.remove(unsparse_image)return mkfs_output

6.总结

至此,Android10.0中的image打包过程基本上理清了,过程比较绕,相互依赖比较多,逐层解开后,就比较好理解了。

接下来我们再一起了解下kati、blueprint、ninja的相关知识,了解一下编译系统具体是怎么玩的。

我的微信公众号:IngresGe

Image打包流程-Android10.0编译系统(四)相关推荐

  1. 编译系统总结篇-Android10.0编译系统(十一)

    摘要:Android10.0编译系统总结 阅读本文大约需要花费20分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢! 欢迎关 ...

  2. Ninja提升编译速度的方法-Android10.0编译系统(十)

    摘要:如何通过Ninja来提升Android编译速度 阅读本文大约需要花费10分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢 ...

  3. Ninja简介-Android10.0编译系统(九)

    摘要:Ninja具体干了什么? 阅读本文大约需要花费16分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢! 欢迎关注我的公众 ...

  4. Android.bp 语法浅析-Android10.0编译系统(八)

    摘要:Blueprint解析Android.bp到ninja的代码流程时如何走的? 阅读本文大约需要花费18分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Andro ...

  5. Blueprint代码详细分析-Android10.0编译系统(七)

    摘要:Blueprint解析Android.bp到ninja的代码流程时如何走的? 阅读本文大约需要花费18分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Andro ...

  6. Blueprint简介-Android10.0编译系统(六)

    摘要:Android.bp由Blueprint进行解析翻译,最终通过soong build编译成ninja文件,那么Blueprint是什么呢? 阅读本文大约需要花费10分钟. 文章首发微信公众号:I ...

  7. Kati详解-Android10.0编译系统(五)

    摘要:本节主要来讲解Kati把Makefile编译成build-xxx.ninja,那么Kati是什么? 是如何工作的呢? 阅读本文大约需要花费24分钟. 文章首发微信公众号:IngresGe 专注于 ...

  8. make编译过程-Android10.0编译系统(三)

    摘要:本节主要来进行Android10.0 编译系统的make过程 阅读本文大约需要花费29分钟. 文章首发微信公众号:IngresGe 专注于Android系统级源码分析,Android的平台设计, ...

  9. HIDL示例-JAVA服务创建-Client验证-Android10.0 HwBinder通信原理(四)

    摘要:本节主要来讲解Android10.0 JAVA层的HIDL服务创建和JAVA层的Client验证 阅读本文大约需要花费15分钟. 文章首发微信公众号:IngresGe 专注于Android系统级 ...

最新文章

  1. PHP 通过数组判断数组顺序输出是否是二叉排序树的后序遍历结果
  2. ViewPager+Fragment切换时无法更新数据问题解析(源代码分享)
  3. [CF1066C]Books Queries
  4. jQuery 追加元素的方法如append、prepend、before
  5. python 项目管理_【Python基础(十三)】文件的操作(II)
  6. [译]震惊!RxJava 5 个不为人知的小秘密
  7. linux线程引起jvm崩溃,JVM宕机分析
  8. 关于灵魂安放,年轻人如何选择适合自己的城市呢?学长有话说
  9. 轻松实现SQL Server与Access、Excel数据表间的导入导出
  10. 嘉兴市计算机技工学校,嘉兴市高级技工学校2021年招生简章
  11. linux ssh客户端乱码,Win10专业版下Open ssh客户端乱码咋办?
  12. cd40系列芯片_IC集成电路型号大全及40系列芯片功能大全
  13. 【气动学】基于matlab导弹拦截计算方法【含Matlab源码 982期】
  14. 接口测试搭建之JMeter接口测试与SoapUI接口测试
  15. 互联网日报 | 8月3日 星期二 | 陌陌宣布更名为“Hello”集团;国家电网升至世界500强第二位;比亚迪总市值超8300亿...
  16. 一个下载网页视频的方法
  17. termux自动启动ssh
  18. 产品助理入门攻略(一枚入行3年的PM内心独白)
  19. 87.3 laravel中常见问题以及解决方案
  20. 8位数:数字、小写字母字典思考

热门文章

  1. 要了薪资证明了录取的几率大吗_空乘专业的录取分数线是多少?
  2. python跟java 效率_Python与Java:哪个更好,如何选择?
  3. ffmpeg php win32,解决PHP5.3.x下ffmpeg安装配置问题
  4. crowd counting_[Crowd_Counting]-PGCNet-ICCV2019
  5. 菜鸟成长之路05/06/07
  6. 面向对象课程第二单元作业总结
  7. oracle 9i net基本配置
  8. vue绑定数据之前 会看到源代码
  9. laravel框架路由设置404方式
  10. JAVA学习--反射属性操作