RK3588 AB镜像升级学习(一)

2024-05-31 16:04
文章标签 镜像 学习 升级 rk3588 ab

本文主要是介绍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系统无关,主要有厂家决定

这篇关于RK3588 AB镜像升级学习(一)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1018388

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件