RK3588 AB镜像升级学习(一)

参考资料:Android A/B 系统_洛奇看世界的博客-CSDN博客

一、AB镜像分区


区分了OTA升级镜像的两种方式:

  • 传统的升级方式:设备有Android系统和Recovery系统,如果Android需要升级时,把内容存到cache分区。重启后进入recovery系统,从cache分区更新Android系统。
  • A/B的升级方式:由Android后台的update_engine和两套slot A和slot B组成,update_engine监测升级信息,并下载升级数据,会把数据更新到非运行的分区,写完后从更新的分区启动。

系统的分区

说明引导linuxAndroid主系统的linux kernel文件和挂载system和其他分区的ramdiskAndroid系统分区(应用程序、库文件)厂商定制应用和库文件用户数据分区临时数据分区,存放OTA存放Recovery系统的linux kernel和ramdisk存放Android主系统和Recovery系统跟bootloader通信数据
传统升级分区bootloaderbootsystemvendoruserdatacacherecoverymisc
A/B升级分区bootloaderboot_a
boot_b
system_a
system_b
vendor_a
vendor_b
userdata//misc

区别:
系统:①只有一套分区,②由两套分区
bootloader交互方式:①读取misc分区信息来进入主系统或Recovery系统,②通过特定的分区信息来决定进A或B
编译:①会生成boot.img和recovery.img,②只生成boot.img
OTA更新包生成方式:生成方式是一样的,②的内容不一样

系统分区属性


在Android的AB(A/B)分区升级机制中,每个分区确实有一些关键的属性,用于管理分区的状态和升级过程。这些属性通常包括:

  • Active:表示当前正在使用的分区。在任何给定时间,只有一个分区是活动的。
  • Bootable:表示分区是否可以启动。这通常用于确定分区是否已经准备好被引导加载程序启动。
  • Successful:表示分区上次尝试启动是否成功。这个属性用于确定如果设备重启,是否应该继续使用当前分区,或者是否应该回滚到另一个分区。

这些属性通常由引导加载程序(如fastboot)和系统更新机制(如update_engine)管理。它们用于确保系统的稳定性和可靠性,以及在更新失败时能够安全地回滚到之前的版本。
在AB分区升级机制中,这些属性通常存储在分区的元数据中,或者在引导加载程序的配置中。它们用于指导系统的升级和回滚过程,确保设备始终能够启动到一个稳定、可靠的系统版本。

  1. 普通场景(Normal cases):

A分区:灰色方框,表示当前没有用,设置为bootable和successful。
B分区:绿色方框,表示当前正在使用,设置为active、bootable和successful。

  1. 升级中(Update in progress):

A分区:灰色方框,表示正在进行升级,设置为unbootable和清除successful标识。
B分区:绿色方框,表示当前正在使用,保持为active、bootable和successful。

  1. 更新完成,等待重启(Update applied, reboot pending):

A分区:灰色方框,表示升级完成,设置为bootable和active,但没有设置successful。
B分区:灰色方框,表示等待重启,设置为bootable和successful,但没有active。

  1. 从新系统成功启动(System rebooted into new update):

A分区:绿色方框,表示重启后从A分区启动,设置为active、bootable和successful。
B分区:灰色方框,表示当前没有用,设置为bootable和successful,但没有active。


参考资料:Android A/B System OTA分析(二)系统image的生成_android新建分区如何生成image-CSDN博客

二、A/B镜像相关的Makefile变量


A/B系统必须定义的变量

  • AB_OTA_UPDATER := true

A/B系统的主要开关变量,设置后

  • recovery系统内不再具有操作cache分区的功能,bootable/recovery/recovery_ui/device.cpp
  • recovery系统使用不同的方式来解析升级文件,bootable/recovery/install/install.cpp
  • 生成A/B系统相关的META文件
  • AB_OTA_PARTITIONS := boot system vendor

将A/B系统可升级的分区写入文件_$(zip_root)/META/ab_partitions.txt_

  • BOARD_BUILD_SYSTEM_ROOT_IMAGE := true

将boot ramdisk放到system分区内

  • TARGET_NO_RECOVERY := true

不再生成recovery.img镜像

  • BOARD_USES_RECOVERY_AS_BOOT := true

将recovery ramdisk放到boot.img文件内

  • PRODUCT_PACKAGES += update_engine update_verifier

编译update_engine和update_verifier模块,并安装相应的应用

A/B系统可选定义的变量

  • PRODUCT_PACKAGES_DEBUG += update_engine_client

系统自带了一个update_engine_client应用,可以根据需要选择是否编译并安装

A/B系统不能定义的变量

  • BOARD_RECOVERYIMAGE_PARTITION_SIZE

系统没有recovery分区,不需要设置recovery分区的SIZE

  • BOARD_CACHEIMAGE_PARTITION_SIZE

系统没有cache分区,不需要设置cache分区的SIZE

  • BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE

系统没有cache分区,不需要设置cache分区的TYPE

A/B系统镜像文件的生成
build/core/Makefile定义了所需生成的镜像目标和规则,各镜像规则如下,我直接在代码里进行注释了。

recovery.img

ifneq ($(TARGET_NO_RECOVERY),true)$(hide) cp $(TARGET_PREBUILT_RESOURCE) $(zip_root)/RECOVERY/resource.img
endif

由于A/B系统定了TARGET_NO_RECOVERY := true,这里INSTALLED_RECOVERYIMAGE_TARGET被设置为空,所以不会生成recovery.img

boot.img

ifneq ($(strip $(BOARD_KERNEL_BINARIES)),)		# 检查BOARD_KERNEL_BINARIES变量是否为空
# 将BOARD_KERNEL_BINARIES变量中的kernel替换为boot
# 如果有多个内核镜像,则为每个内核镜像生成一个boot.img文件。BUILT_BOOTIMAGE_TARGET := $(foreach k,$(subst kernel,boot,$(BOARD_KERNEL_BINARIES)), $(PRODUCT_OUT)/$(k).img)
else
# 如果没有多个内核镜像,则只生成一个boot.img文件。BUILT_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img
endif
INTERNAL_PREBUILT_BOOTIMAGE :=
# 遍历PRODUCT_PACKAGES变量中的所有包,检查它们是否包含EXTRACTED_BOOT_IMAGE属性,如果有,则将其添加到my_installed_prebuilt_gki_apex变量中
my_installed_prebuilt_gki_apex := $(strip $(foreach package,$(PRODUCT_PACKAGES),$(if $(ALL_MODULES.$(package).EXTRACTED_BOOT_IMAGE),$(package))))
ifdef my_installed_prebuilt_gki_apexifneq (1,$(words $(my_installed_prebuilt_gki_apex))) # len(my_installed_prebuilt_gki_apex) > 1# my_installed_prebuilt_gki_apex中包含预构建GKI APEX镜像数量大于1就报错$(error More than one prebuilt GKI APEXes are installed: $(my_installed_prebuilt_gki_apex))endif # len(my_installed_prebuilt_gki_apex) > 1ifdef BOARD_PREBUILT_BOOTIMAGE# 如果BOARD_PREBUILT_BOOTIMAGE已经被定义,则提示已经有预构建GKI APEX镜像$(error Must not define BOARD_PREBUILT_BOOTIMAGE because a prebuilt GKI APEX is installed: $(my_installed_prebuilt_gki_apex))endif # BOARD_PREBUILT_BOOTIMAGE defined# 获取已安装的预构建GKI APEX镜像的提取boot.img文件的路径my_apex_extracted_boot_image := $(ALL_MODULES.$(my_installed_prebuilt_gki_apex).EXTRACTED_BOOT_IMAGE)# 定义boot.img文件的目标路径INSTALLED_BOOTIMAGE_TARGET := $(PRODUCT_OUT)/boot.img# 将提取的boot.img文件复制到目标路径$(eval $(call copy-one-file,$(my_apex_extracted_boot_image),$(INSTALLED_BOOTIMAGE_TARGET)))# 将提取的boot.img文件设置为内部预构建的boot.img文件INTERNAL_PREBUILT_BOOTIMAGE := $(my_apex_extracted_boot_image)else # my_installed_prebuilt_gki_apex not defined# $1: boot image target
# returns the kernel used to make the bootimage
define bootimage-to-kernel$(if $(BOARD_KERNEL_BINARIES),\$(PRODUCT_OUT)/$(subst .img,,$(subst boot,kernel,$(notdir $(1)))),\	#获取用于制作boot.img文件的内核的路径$(INSTALLED_KERNEL_TARGET)) # 如果BOARD_KERNEL_BINARIES变量没有被定义,则使用INSTALLED_KERNEL_TARGET变量中的内核路径
endefifdef BOARD_BOOTIMAGE_PARTITION_SIZEBOARD_KERNEL_BOOTIMAGE_PARTITION_SIZE := $(BOARD_BOOTIMAGE_PARTITION_SIZE)
endif

代码用于处理预构建的GKI APEX镜像的安装和配置。如果安装了预构建的GKI APEX镜像,则将其提取的boot.img文件复制到目标路径,并设置为内部预构建的boot.img文件。如果没有安装预构建的GKI APEX镜像,则使用INSTALLED_KERNEL_TARGET变量中的内核路径来制作boot.img文件

# ....如果没有定义BOARD_PREBUILT_BOOTIMAGE则INSTALLED_BOOTIMAGE_TARGET为空
else # BOARD_PREBUILT_BOOTIMAGE则 not defined
INSTALLED_BOOTIMAGE_TARGET :=
endif # BOARD_PREBUILT_BOOTIMAGE
endif # TARGET_NO_KERNEL
endif # my_installed_prebuilt_gki_apex not defined
# ...# 这段代码用于处理当设备使用恢复模式作为启动模式时的构建过程。
# 它为boot.img文件添加依赖关系,并使用recoveryimage-deps变量中的文件作为依赖关系。
# 然后,它调用build-recoveryimage-target宏来构建boot.img文件,
# 并将用于制作boot.img文件的内核路径作为参数传递给宏。
ifeq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
$(foreach b,$(INSTALLED_BOOTIMAGE_TARGET), $(eval $(call add-dependency,$(b),$(call bootimage-to-kernel,$(b)))))
$(INSTALLED_BOOTIMAGE_TARGET): $(recoveryimage-deps)$(call pretty,"Target boot image from recovery: $@")$(call build-recoveryimage-target, $@, $(PRODUCT_OUT)/$(subst .img,,$(subst boot,kernel,$(notdir $@))))
endif # BOARD_USES_RECOVERY_AS_BOOT# 再来看看原本的recovery.img的生成规则:
#  - A/B 系统下,INSTALLED_RECOVERYIMAGE_TARGET已经定义为空,什么都不做
#  - 非A/B 系统下,以下规则会生成recovery.img
$(INSTALLED_RECOVERYIMAGE_TARGET): $(recoveryimage-deps)$(call build-recoveryimage-target, $@, \$(if $(filter true, $(BOARD_EXCLUDE_KERNEL_FROM_RECOVERY_IMAGE)),, $(recovery_kernel)))

在Makefile的开头可以看到:

INSTALLED_RECOVERYIMAGE_TARGET :=
# Build recovery image if
# BUILDING_RECOVERY_IMAGE && !BOARD_USES_RECOVERY_AS_BOOT && !BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT.
# If BOARD_USES_RECOVERY_AS_BOOT is true, leave empty because INSTALLED_BOOTIMAGE_TARGET is built
#   with recovery resources.
# If BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT is true, leave empty to build recovery resources
#   but not the final recovery image.
# 满足条件时会生成recovery.img
ifdef BUILDING_RECOVERY_IMAGE
ifneq ($(BOARD_USES_RECOVERY_AS_BOOT),true)
ifneq ($(BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT),true)
INSTALLED_RECOVERYIMAGE_TARGET := $(PRODUCT_OUT)/recovery.img
endif
endif
endif

从上面生成boot.img和recovery.img:
如果是AB升级的镜像:

  • BOARD_USES_RECOVERY_AS_BOOT:这个参数通常被设置为true,表示设备使用恢复模式作为启动模式。在这种情况下,不需要单独的recovery.img文件,因为恢复模式的功能已经被集成到启动镜像中。
  • BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT:这个参数通常被设置为true,表示设备的恢复资源被移动到供应商启动分区。在这种情况下,也不需要单独的recovery.img文件。
  • BUILDING_RECOVERY_IMAGE:这个参数通常被设置为false,因为不需要生成单独的recovery.img文件。

如果不是AB升级的镜像:

  • BOARD_USES_RECOVERY_AS_BOOT:这个参数通常被设置为false,表示设备不使用恢复模式作为启动模式。在这种情况下,需要生成单独的recovery.img文件。
  • BOARD_MOVE_RECOVERY_RESOURCES_TO_VENDOR_BOOT:这个参数通常被设置为false,表示设备的恢复资源不移动到供应商启动分区。在这种情况下,需要生成单独的recovery.img文件。
  • BUILDING_RECOVERY_IMAGE:这个参数通常被设置为true,因为需要生成单独的recovery.img文件。

总的来说,在AB升级机制中,通常不需要生成单独的recovery.img文件,因此相关参数会被设置为true。在非AB升级机制中,需要生成单独的recovery.img文件,因此相关参数会被设置为false

对比A/B系统下boot.img生成方式和非A/B系统下recovery.img的生成方式,基本上是一样的,所以A/B系统下的boot.img相当于非A/B系统下的recovery.img。

system.img

# $(1): output file
define build-systemimage-target@echo "Target system fs image: $(1)"# 创建必要的目录,并删除旧的系统镜像信息文件@mkdir -p $(dir $(1)) $(systemimage_intermediates) && rm -rf $(systemimage_intermediates)/system_image_info.txt# 调用call generate-userimage-prop-dictionary,重新生成系统属性文件system_image_info.txt$(call generate-image-prop-dictionary, $(systemimage_intermediates)/system_image_info.txt,system, \skip_fsck=true)PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \$(BUILD_IMAGE) \	# 调用BUILD_IMAGE制作镜像# 传递必要的参数给BUILD_IMAGE命令,包括目标输出目录、系统镜像信息文件、系统镜像文件的目标路径和目标输出目录。$(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命令失败,则执行错误处理# 复制已安装文件列表到分布目录,作为备份
endefifeq ($(BOARD_AVB_ENABLE),true)
$(BUILT_SYSTEMIMAGE): $(BOARD_AVB_SYSTEM_KEY_PATH)
endif
$(BUILT_SYSTEMIMAGE): $(FULL_SYSTEMIMAGE_DEPS) $(INSTALLED_FILES_FILE)$(call build-systemimage-target,$@)
# 这行代码定义系统镜像文件的目标路径
INSTALLED_SYSTEMIMAGE_TARGET := $(PRODUCT_OUT)/system.img
# 这行代码定义系统镜像文件的源目录路径
SYSTEMIMAGE_SOURCE_DIR := $(TARGET_OUT)

这个BUILD_IMAGE在build/core/config.mk:571被定义:

BUILD_IMAGE := $(HOST_OUT_EXECUTABLES)/build_image$(HOST_EXECUTABLE_SUFFIX)

然后用find ./build/ -name "build_image*"只找到build_image.py命令

./build/make/tools/releasetools/build_image.py

所以应该是调用build_image.py,根据系统属性文件system_image_info.txt和system目录$(PRODUCT_OUT)/system创建system.img文件

build_image.py的程序入口

 main(argv):if len(argv) != 4:print(__doc__)sys.exit(1)common.InitLogging()in_dir = argv[0]				# TARGET_OUTglob_dict_file = argv[1]		# $(systemimage_intermediates)/system_image_info.txtout_file = argv[2]			# $(1)target_out = argv[3]			# $(TARGET_OUT)# 解析系统属性的字典文件system_image_info.txtglob_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 = ""# 设置system.img的挂载点为systemif image_filename == "system.img":mount_point = "system"# ...else:logger.error("Unknown image file name %s", image_filename)sys.exit(1)image_properties = ImagePropFromGlobalDict(glob_dict, mount_point)try: # 调用BuildImage函数来创建文件BuildImage(in_dir, image_properties, out_file, target_out)except:logger.error("Failed to build %s from %s", out_file, in_dir)raiseif __name__ == '__main__':try:main(sys.argv[1:])finally:common.Cleanup() 

对应的BuildImage函数用了BuildImageMkfs来创建文件系统,最后调用了e2fsck来创建文件系统

def BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config):"""Builds a pure 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.fs_config: The fs_config file that drives the prototypeRaises:BuildImageError: On build image failures."""build_command = []fs_type = prop_dict.get("fs_type", "")run_e2fsck = Falseneeds_projid = prop_dict.get("needs_projid", 0)needs_casefold = prop_dict.get("needs_casefold", 0)needs_compress = prop_dict.get("needs_compress", 0)if fs_type.startswith("ext"):		# 如果是ext格式,调用mkuserimg工具来创建文件系统镜像,并根据不同的参数来指定镜像的属性。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 prop_dict.get("ext4_share_dup_blocks") == "true":build_command.append("-c")if (needs_projid):build_command.extend(["--inode_size", "512"])else:build_command.extend(["--inode_size", "256"])if "selinux_fc" in prop_dict:build_command.append(prop_dict["selinux_fc"])elif fs_type.startswith("erofs"): # 如果是erofs格式build_command = ["mkerofsimage.sh"]build_command.extend([in_dir, out_file])# ...elif fs_type.startswith("squash"):	# 如果是squash格式build_command = ["mksquashfsimage.sh"]# ...elif fs_type.startswith("f2fs"):	# 如果是f2fs格式# ...else:raise BuildImageError("Error: unknown filesystem type: {}".format(fs_type))try:		# 构建systemmkfs_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"print("Out of space? Out of inodes? The tree size of {} is {}, ""with reserved space of {} bytes ({} MB).".format(in_dir, du_str,int(prop_dict.get("partition_reserved_size", 0)),int(prop_dict.get("partition_reserved_size", 0)) // BYTES_IN_MB))if ("image_size" in prop_dict and "partition_size" in prop_dict):print("The max image size for filesystem files is {} bytes ({} MB), ""out of a total partition size of {} bytes ({} MB).".format(int(prop_dict["image_size"]),int(prop_dict["image_size"]) // BYTES_IN_MB,int(prop_dict["partition_size"]),int(prop_dict["partition_size"]) // BYTES_IN_MB))raiseif run_e2fsck and prop_dict.get("skip_fsck") != "true":# 将稀疏镜像文件转换为未稀疏的镜像文件unsparse_image = UnsparseImage(out_file, replace=False)# Run e2fsck on the inflated image file 运行e2fsck工具对未稀疏的镜像文件进行检查e2fsck_command = ["e2fsck", "-f", "-n", unsparse_image]try:common.RunAndCheckOutput(e2fsck_command)finally:os.remove(unsparse_image)return mkfs_output

userdata.img

# -----------------------------------------------------------------
# data partition image
INTERNAL_USERDATAIMAGE_FILES := \$(filter $(TARGET_OUT_DATA)/%,$(ALL_DEFAULT_INSTALLED_MODULES))
# 如果定义了BUILDING_USERDATA_IMAGE,所以这里会定义userdata.img并生成这个文件
ifdef BUILDING_USERDATA_IMAGE                                                                                             
userdataimage_intermediates := \$(call intermediates-dir-for,PACKAGING,userdata)
BUILT_USERDATAIMAGE_TARGET := $(PRODUCT_OUT)/userdata.img
# 具体生成userdata.img的宏函数                                                                                                                          
define build-userdataimage-target$(call pretty,"Target userdata fs image: $(INSTALLED_USERDATAIMAGE_TARGET)")@mkdir -p $(TARGET_OUT_DATA)@mkdir -p $(userdataimage_intermediates) && rm -rf $(userdataimage_intermediates)/userdata_image_info.txt$(call generate-image-prop-dictionary, $(userdataimage_intermediates)/userdata_image_info.txt,userdata,skip_fsck=true)PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \$(BUILD_IMAGE) \$(TARGET_OUT_DATA) $(userdataimage_intermediates)/userdata_image_info.txt \$(INSTALLED_USERDATAIMAGE_TARGET) $(TARGET_OUT)$(call assert-max-image-size,$(INSTALLED_USERDATAIMAGE_TARGET),$(BOARD_USERDATAIMAGE_PARTITION_SIZE))
endef
# 好吧,这里才是真正调用build-userdataimage-target去生成userdata.img的规则
# We just build this directly to the install location.
INSTALLED_USERDATAIMAGE_TARGET := $(BUILT_USERDATAIMAGE_TARGET)
INSTALLED_USERDATAIMAGE_TARGET_DEPS := \$(INTERNAL_USERIMAGES_DEPS) \$(INTERNAL_USERDATAIMAGE_FILES)
$(INSTALLED_USERDATAIMAGE_TARGET): $(INSTALLED_USERDATAIMAGE_TARGET_DEPS)$(build-userdataimage-target)

这里的步骤跟生成system.img基本一致,宏函数build-userdataimage-target内通过build_image.py来将$(PRODUCT_OUT)/data目录内容打包生成userdata.img,不同的是,这里不再需要放入ramdisk的内容。
显然,userdata.img的生成跟是否是A/B系统没有关系。

cache.img

# -----------------------------------------------------------------
# cache partition image
# `A/B`系统中 BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE 没有定义,这里条件不能满足,所以不会生成cache.img
ifdef BUILDING_CACHE_IMAGE
INTERNAL_CACHEIMAGE_FILES := \$(filter $(TARGET_OUT_CACHE)/%,$(ALL_DEFAULT_INSTALLED_MODULES))cacheimage_intermediates := \$(call intermediates-dir-for,PACKAGING,cache)
BUILT_CACHEIMAGE_TARGET := $(PRODUCT_OUT)/cache.imgdefine build-cacheimage-target$(call pretty,"Target cache fs image: $(INSTALLED_CACHEIMAGE_TARGET)")@mkdir -p $(TARGET_OUT_CACHE)@mkdir -p $(cacheimage_intermediates) && rm -rf $(cacheimage_intermediates)/cache_image_info.txt$(call generate-image-prop-dictionary, $(cacheimage_intermediates)/cache_image_info.txt,cache,skip_fsck=true)PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \$(BUILD_IMAGE) \$(TARGET_OUT_CACHE) $(cacheimage_intermediates)/cache_image_info.txt \$(INSTALLED_CACHEIMAGE_TARGET) $(TARGET_OUT)$(call assert-max-image-size,$(INSTALLED_CACHEIMAGE_TARGET),$(BOARD_CACHEIMAGE_PARTITION_SIZE))
endef# We just build this directly to the install location.
# 这里是真正去生成cache.img的地方,可惜`A/B`系统下不会再有调用了
INSTALLED_CACHEIMAGE_TARGET := $(BUILT_CACHEIMAGE_TARGET)
$(INSTALLED_CACHEIMAGE_TARGET): $(INTERNAL_USERIMAGES_DEPS) $(INTERNAL_CACHEIMAGE_FILES)$(build-cacheimage-target)

于A/B系统定了没有定义BOARD_CACHEIMAGE_FILE_SYSTEM_TYPE,这里BUILT_CACHEIMAGE_TARGET也不会定义,所以不会生成cache.img

vendor.img

# -----------------------------------------------------------------
# vendor partition image
# 如果系统内有定义BUILDING_VENDOR_IMAGE,则这里会生成vendor.img
ifdef BUILDING_VENDOR_IMAGE
# 定义vendor系统内包含的所有文件
INTERNAL_VENDORIMAGE_FILES := \$(filter $(TARGET_OUT_VENDOR)/%,\$(ALL_DEFAULT_INSTALLED_MODULES))# Create symlink /vendor/odm to /odm if necessary.
ifdef BOARD_USES_ODMIMAGEINTERNAL_VENDORIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT_VENDOR)/odm,/odm,odm.img)
endif
ifdef BOARD_USES_VENDOR_DLKMIMAGE                                                                                           
INTERNAL_VENDORIMAGE_FILES += $(call create-partition-compat-symlink,$(TARGET_OUT_VENDOR)/lib/modules,/vendor_dlkm/lib/modules,vendor_dlkm.img)
endif
# vendor的文件列表:installed-files-vendor.txt
INSTALLED_FILES_FILE_VENDOR := $(PRODUCT_OUT)/installed-files-vendor.txt
INSTALLED_FILES_JSON_VENDOR := $(INSTALLED_FILES_FILE_VENDOR:.txt=.json)
$(INSTALLED_FILES_FILE_VENDOR): .KATI_IMPLICIT_OUTPUTS := $(INSTALLED_FILES_JSON_VENDOR)
$(INSTALLED_FILES_FILE_VENDOR) : $(INTERNAL_VENDORIMAGE_FILES) $(FILESLIST) $(FILESLIST_UTIL)@echo Installed file list: $@mkdir -p $(dir $@)rm -f $@$(FILESLIST) $(TARGET_OUT_VENDOR) > $(@:.txt=.json)$(FILESLIST_UTIL) -c $(@:.txt=.json) > $@
# vendor.img目标
vendorimage_intermediates := \$(call intermediates-dir-for,PACKAGING,vendor)
BUILT_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img
# 定义生成vendor.img的宏函数build-vendorimage-target
define build-vendorimage-target$(call pretty,"Target vendor fs image: $(INSTALLED_VENDORIMAGE_TARGET)")@mkdir -p $(TARGET_OUT_VENDOR)@mkdir -p $(vendorimage_intermediates) && rm -rf $(vendorimage_intermediates)/vendor_image_info.txt$(call generate-image-prop-dictionary, $(vendorimage_intermediates)/vendor_image_info.txt,vendor,skip_fsck=true)PATH=$(INTERNAL_USERIMAGES_BINARY_PATHS):$$PATH \$(BUILD_IMAGE) \$(TARGET_OUT_VENDOR) $(vendorimage_intermediates)/vendor_image_info.txt \$(INSTALLED_VENDORIMAGE_TARGET) $(TARGET_OUT)$(call assert-max-image-size,$(INSTALLED_VENDORIMAGE_TARGET) $(RECOVERY_FROM_BOOT_PATCH),$(BOARD_VENDORIMAGE_PARTITION_SIZE))
endef# We just build this directly to the install location.
# 生成vendor.img的依赖和规则
INSTALLED_VENDORIMAGE_TARGET := $(BUILT_VENDORIMAGE_TARGET)
$(INSTALLED_VENDORIMAGE_TARGET): \$(INTERNAL_USERIMAGES_DEPS) \$(INTERNAL_VENDORIMAGE_FILES) \$(INSTALLED_FILES_FILE_VENDOR) \$(RECOVERY_FROM_BOOT_PATCH)$(build-vendorimage-target).PHONY: vendorimage-nodeps vnod
vendorimage-nodeps vnod: | $(INTERNAL_USERIMAGES_DEPS)$(build-vendorimage-target)sync: $(INTERNAL_VENDORIMAGE_FILES)
# 如果定义了BOARD_PREBUILT_VENDORIMAGE,说明已经预备好了vendor.img,那就直接复制到目标位置
else ifdef BOARD_PREBUILT_VENDORIMAGE
INSTALLED_VENDORIMAGE_TARGET := $(PRODUCT_OUT)/vendor.img
$(eval $(call copy-one-file,$(BOARD_PREBUILT_VENDORIMAGE),$(INSTALLED_VENDORIMAGE_TARGET)))
endif

显然,vendor.img跟是否是A/B系统没有关系,主要看系统是否定义了BUILDING_VENDOR_IMAGE。

总结:

  • recovery.img,不再单独生成,传统方式的recovery.img现在叫做boot.img
  • boot.img,包含kernel和recovery模式的ramdisk
  • system.img,传统方式下system.img由 ( P R O D U C T O U T ) / s y s t e m 文件夹打包而成, A / B 系统下,制作时将 (PRODUCT_OUT)/system文件夹打包而成,A/B系统下,制作时将 (PRODUCTOUT)/system文件夹打包而成,A/B系统下,制作时将(PRODUCT_OUT)/root和$(PRODUCT_OUT)/system合并到一起,生成一个完整的带有rootfs的system.img
  • userdata.img,跟原来一样,打包$(PRODUCT_OUT)/data文件夹而成
  • cache.img,A/B系统下不再单独生成cache.img
  • vendor.img,文件的生成跟是否A/B系统无关,主要有厂家决定

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/22422.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P3. 创建个人中心页面

P3. 创建个人中心页面 0 概述Tips1 个人中心页面1.1 创建 Bot 表及 pojo, mapper1.2 实现 Bot 增删改查的 API1.3 实现个人中心页面前端 0 概述 主要介绍了一下添加一个表(类),及其CRUD的前端和后端的实现方式,介绍的是通用的方法。 后端的CRUD很好写&am…

5 - 无效的推文(高频 SQL 50 题基础版)

5. 无效的推文 知识点:计算字符长度 -- 查询所有无效推文的编号(ID) -- CHAR_LENGTH() 或 LENGTH() 函数来计算列中字符串的字符数。 -- 这两个函数的区别在于处理非 ASCII 字符时的行为: -- CHAR_LENGTH() 返回字符串的字符数&a…

可能是当下最能打的MCU图形库:LVGL

在讨论图形用户界面(GUI)库时,很多人会想到emWin、TouchGFX以及QT等。这些库虽然功能强大,但它们普遍存在一个共同的问题:对资源的需求较高,不适用于资源有限的微控制器(MCU)。有没有…

Spring Boot既打jar包又打war包如何做

你好,我是柳岸花开。 引言 在软件开发中,根据不同的部署需求,我们可能需要将应用打包成不同的格式。Spring Boot作为目前流行的Java应用开发框架,提供了一种简单的方式来打包应用。本文将介绍如何利用Maven Profiles在Spring Boot…

【linux】swap学习

在 Linux 系统中,swap 是一种用于扩展系统内存的技术。当物理内存(RAM)不足时,系统会将一部分不常用的内存数据移至 swap 空间,从而释放物理内存供其他程序使用。Swap 空间可以是一个单独的分区(swap 分区&…

交互规范:苹果 iOS 11 设计规范

文件格式:PDF(请与班主任联系获取原型文档) 文件名称:苹果 iOS 11 设计规范 文件大小:29.2 MB 文档内容介绍 免费领取资料 添加班主任回复 “210421” 领取

数据结构_手撕七大排序(快排,归并,堆排,希尔,选择,插入,冒泡)

✨✨所属专栏:数据结构✨✨ ✨✨作者主页:嶔某✨✨ 排序的概念 排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。 稳定性:假定在待排序的记录序…

企业数据安全管理容易忽视的关键点:云存储权限管控

云存储已经广泛应用于企业用户、教育领域、医疗领域以及政府和公共服务部门。具体应用场景包括文件共享、数据备份、在线课程、教学资源库、电子病历、医学影像、实验室数据、政务数据的集中管理和共享等。 云存储的优势非常明显: 可扩展性:云存储空间可…

Nginx漏洞解析及复现

Nginx漏洞 Nginx能做到正向代理、反向代理、负载均衡、HTTP服务器等,强大的功能不言而喻,但也伴随着使用 上的风险,深入理解Nginx的漏洞有助于创建安全的业务系统。 Nginx解析漏洞 漏洞原理 Nginx的解析漏洞的出现和Nginx的版本没有关系&…

基于深度学习的中文标点预测模型-中文标点重建(Transformer模型)【已开源】

基于深度学习的中文标点预测模型-中文标点重建(Transformer模型)提供模型代码和训练好的模型 前言 目前以深度学习对文本自动添加标点符号研究很少,已知的开源项目并不多,详细的介绍就更少了,但对文本自动添加标点符号…

三菱MR-J4系列伺服驱动器E7.1和32.3故障报警处理总结

三菱MR-J4系列伺服驱动器E7.1和32.3故障报警处理总结 三菱MR-J4系列伺服驱动器出现报警,故障代码为:E7.1和32.3,查阅手册可以看到E7.1和32.3的报警解释信息, 如下图所示,此时简单运动控制模块上的ERROR灯亮, 如下图所示,用GX WORKS3打开备份程序,找到FX5-80SSC-…

3 - 大的国家(高频 SQL 50 题基础版)

3.大的国家 -- 查询属性:国家名称、人口和面积 select name,population,area fromWorld where area>3000000 OR population>25000000;

组件框架信息泄露

后端spring-boot框架 actuator组件信息泄露 Actuator是Spring-Boot提供的服务监控和管理中间件,默认配置会出现接口未授权 访问,部分接口会泄露网站流量信息和内存信息等,使用Jolokia库特性甚至可以远程执行任意代码,获 取服务器…

科技赋能,无障碍出行的新纪元

在现代社会,公共设施的建设不仅是衡量城市文明程度的标尺,更是实现社会公平与包容的重要载体。对于盲人群体而言,一个完善的公共设施网络,意味着他们能够更加独立、自信地融入社会,享受与视力健全者同等的公共服务与便…

使用onnxruntime加载YOLOv8生成的onnx文件进行目标检测

在网上下载了60多幅包含西瓜和冬瓜的图像组成melon数据集,使用 LabelMe 工具进行标注,然后使用 labelme2yolov8 脚本将json文件转换成YOLOv8支持的.txt文件,并自动生成YOLOv8支持的目录结构,包括melon.yaml文件,其内容…

干货!如何在Jmeter中实现对NCR响应的解析

最近做接口测试时发现了一个问题,部分请求的响应是通过NCR编码实现的,这样就导致了无法对这些请求进行断言,为了解决这个问题进行了如下调研,大家可以参考下面两篇文章: 使用Java apache commons包五分钟搞定NCR解析&…

CCIG 2024:大模型技术及其前沿应用论坛深度解析

一、CCIG论坛介绍 中国图象图形大会(CCIG 2024)是一场备受瞩目的学术盛会,近期在陕西省西安市曲江国际会议中心举行。这次会议以“图聚智生,象合慧成”为主题,由中国图象图形学学会主办,旨在汇聚图像图形领…

ABAP 长文本编辑器弹窗控件

前言 用户想在ALV上编辑长文本,但是ALV只有128个字符肯定是不够用的,所以需要用一个长文本编辑器来输入,本来想自己写的,发现有标准的函数,还挺好用的 代码 在用户双击ALV字段时,触发下述form&#xff0…

使用Rufus工具制作Ubuntu To Go——很详细

一、准备工作 准备工具: 1、下载Rufus(主角)软件 2、准备一个U盘或硬盘(小白128G足够,装Ubuntu系统) 3、下载Ubuntu系统镜像文件 1、下载软件Rufus 先来看一下官网介绍: Rufus 是一款格式化和创建 USB 启动盘的辅助工…

“GPT-4o深度解析:技术演进、能力评估与个人体验综述“

文章目录 每日一句正能量前言对比分析模型架构性能应用场景用户体验技术创新社区和生态系统总结 技术能力语言生成能力语言理解能力技术实现总结 个人感受关于GPT-4o的假设性观点:关于当前语言模型的一般性观点: 后记 每日一句正能量 又回到了原点&#…