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

摘要:本节主要来进行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_dict
  else:
    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 True
    return False

  common_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_point
  if 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_point
  return 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. Values
        will be updated with computed values.
    out_file: The output image file.
    target_out: Path to the TARGET_OUT directory as in Makefile. It actually
        points to the /system directory under PRODUCT_OUT. fs_config (the one
        under system/core/libcutils) reads device specific FS config files from
        there.

  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 = True
  if 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_headroom
    size += reserved_size
    # Round this up to a multiple of 4K so that avbtool works
    size = 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 = False
      if "extfs_sparse_flag" in prop_dict:
        sparse_image = True
      fs_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_size
      reserved_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_headroom
      if free_size <= reserved_size:
        logger.info(
            "Not worth reducing image %d <= %d.", free_size, reserved_size)
      else:
        size -= free_size
        size += reserved_size
        if reserved_size == 0:
          # add .3% margin
          size = 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_size
      extfs_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 greater
      spare_inodes = inodes * 2 // 1000
      min_spare_inodes = 1
      if spare_inodes < min_spare_inodes:
        spare_inodes = min_spare_inodes
      inodes += spare_inodes
      prop_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 = False

  if 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 = True
    build_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-except
      logger.exception("Failed to compute disk usage with du")
      du_str = "unknown"
    ...
    raise

  if run_e2fsck and prop_dict.get("skip_fsck") != "true":
    unsparse_image = UnsparseImage(out_file, replace=False)

    # Run e2fsck on the inflated image file
    e2fsck_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

  • 11
    点赞
  • 56
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值