安卓构建自定义镜像
背景
谷歌提供的aosp都是一些标准的架构,不会完全符合手机厂商的需求。那么手机厂商基于自己的业务需求,自定义镜像就成了不错的拓展方法。
自定义镜像步骤
1、步骤总结
1. 定义描述镜像的makefile
2. 将所有镜像的makefile名字,添加到全局变量PRODUCT_CUSTOM_IMAGE_MAKEFILES
3. 定义需要安装到image的module
4. 将需要安装的module,添加到全局变量CUSTOM_IMAGE_MODULES
5. 将需要拷贝到Image的文件,添加到全局变量CUSTOM_IMAGE_COPY_FILES
2、步骤详解
1. 定义描述镜像的makefile
1. 定义makefile文件名
假设我们定义一个xuanfeng.mk。那么生成的镜像名就是xuanfeng.img
2. 定义makefile内容
- CUSTOM_IMAGE_MOUNT_POINT:=/odm
挂载点, 比如 "oem", "odm" 等
- CUSTOM_IMAGE_PARTITION_SIZE:=1024
分区大小
- CUSTOM_IMAGE_FILE_SYSTEM_TYPE:=ext2
文件系统类型,ext或者erofs格式
- CUSTOM_IMAGE_DICT_FILE:=dict_file
字典文件,build_image.py中BuildImage()用到的一个文本,这里定义了一个字典
- CUSTOM_IMAGE_MODULES:=xuanfengtest
镜像模块列表,把需要安装到镜像的模块,添加到这个列表,可使用CUSTOM_IMAGE_MODULES +=实现
- CUSTOM_IMAGE_COPY_FILES += <src>:<dest>
拷贝文件列表,被定义的拷贝文件将从<src>路径拷贝到镜像的<dest>路径。<dest>路径相对于镜像的根目录
<dest>如果不填,直接放到根目录
- CUSTOM_IMAGE_SELINUX := true
设置镜像是否支持selinux
- CUSTOM_IMAGE_SUPPORT_VERITY := true
设置镜像是否支持avb.
- CUSTOM_IMAGE_SUPPORT_VERITY_FEC := true
设置镜像是否支持FEC (forward error correction)
- CUSTOM_IMAGE_VERITY_BLOCK_DEVICE := /dev/block/…./by-name/odm
提前装载分区,需要配置这个变量
- CUSTOM_IMAGE_AVB_HASH_ENABLE:=true
设置镜像AVB签名时,添加hash footer
- CUSTOM_IMAGE_AVB_ADD_HASH_FOOTER_ARGS:=xxx
设置镜像添加hash footer时的附加参数
- CUSTOM_IMAGE_AVB_HASHTREE_ENABLE:=true
设置镜像avb签名时,添加hashtree footer
- CUSTOM_IMAGE_AVB_ADD_HASHTREE_FOOTER_ARGS:=xxx
设置镜像添加hashtree footer时的附加参数
- CUSTOM_IMAGE_AVB_KEY_PATH:=external/avb/test/data/testkey_rsa2048.pem
设置镜像AVB签名时的私钥
- CUSTOM_IMAGE_AVB_ALGORITHM:=SHA256_RSA2048
设置镜像AVB签名时的算法
2. 将所有镜像的makefile名字,赋值给全局变量PRODUCT_CUSTOM_IMAGE_MAKEFILES
Build System在构建自定义镜像的时候,首先从build_custom_images.mk开始。通过PRODUCT_CUSTOM_IMAGE_MAKEFILES的遍历开始构建
3. 定义需要安装到image的module
1、定义编译使用的Android.mk
2、编写module用到的源码文件,比如.c
3、Android.mk语法参考
https://blog.csdn.net/xuanfengwuxiang/article/details/129306679?spm=1001.2014.3001.5501
4. 将需要安装的module名字,添加到全局变量CUSTOM_IMAGE_MODULES
构建流程走到build_custom_image.mk里的时候,会收集CUSTOM_IMAGE_MODULES里的模块
添加module示例:CUSTOM_IMAGE_MODULES += xuanfeng
5. 将需要直接拷贝到Image的文件,添加到全局变量CUSTOM_IMAGE_COPY_FILES
构建流程走到build_custom_image.mk里的时候,会收集CUSTOM_IMAGE_COPY_FILES里的文件
添加拷贝文件示例:CUSTOM_IMAGE_COPY_FILES += <src>:<dest>
Build System构建自定义镜像的流程
1、构建流程概览
1. build_custom_images.mk遍历所有的自定义镜像的makefile,调用build_custom_image.mk构建
2. build_custom_image.mk收集自定义镜像各种参数,调用build_image.py构建
3. build_image.py解析入参,调用BuildImage()构建
4. BuildImage()构建出纯净镜像,调用avbtool进行签名
5. avbtool签名流程
2、构建流程详情
1. build_custom_images.mk遍历所有的自定义镜像的makefile,调用build_custom_image.mk构建
关键代码如下:
# PRODUCT_CUSTOM_IMAGE_MAKEFILES是个列表,代表所有的自定义镜像makefile
$(foreach mk, $(PRODUCT_CUSTOM_IMAGE_MAKEFILES),\
$(eval my_custom_imag_makefile := $(mk))\
$(eval include $(BUILD_SYSTEM)/tasks/tools/build_custom_image.mk))
2. build_custom_image.mk收集自定义镜像各种参数,调用build_image.py构建
1、根据自定义镜像makefile,解析镜像名称、镜像生成的中间目录、挂载点路径
intermediates := $(call intermediates-dir-for,PACKAGING,$(my_custom_image_name))
my_built_custom_image := $(intermediates)/$(my_custom_image_name).img
# 打包image之前,build system构建出的image内部的内容都放在这个临时文件夹
my_staging_dir := $(intermediates)/$(CUSTOM_IMAGE_MOUNT_POINT)
2、收集CUSTOM_IMAGE_MODULES变量定义安装的模块和PICKUP_FILES
$(foreach m,$(CUSTOM_IMAGE_MODULES),\
...........
# 收集pickup files
$(eval my_pickup_files += $(_pickup_files))\
$(foreach i, $(_built_files),\
.............
$(if $(filter $(TARGET_OUT_ROOT)/%,$(ins)),\
$(eval bui := $(word 1,$(bui_ins)))\
# 收集构建的modules
$(eval my_built_modules += $(bui))\
...........
# 模块中有些也是copy类型
$(eval my_copy_pairs += $(bui):$(my_staging_dir)/$(my_copy_dest)))\
))
3、收集CUSTOM_IMAGE_COPY_FILES变量定义的拷贝文件
my_image_copy_files :=
$(foreach f,$(CUSTOM_IMAGE_COPY_FILES),\
$(eval pair := $(subst :,$(space),$(f)))\
$(eval src := $(word 1,$(pair)))\
$(eval my_image_copy_files += $(src))\
# copy类型文件收集
$(eval my_copy_pairs += $(src):$(my_staging_dir)/$(word 2,$(pair))))
4、拷贝所有文件。也就是my_copy_pairs、my_pickup_files变量收集到的文件
$(my_built_custom_image): PRIVATE_COPY_PAIRS := $(my_copy_pairs)
$(my_built_custom_image): PRIVATE_PICKUP_FILES := $(my_pickup_files)
.........
# 拷贝所有文件
$(hide) $(foreach p,$(PRIVATE_COPY_PAIRS),\
$(eval pair := $(subst :,$(space),$(p)))\
mkdir -p $(dir $(word 2,$(pair)));\
cp -Rf $(word 1,$(pair)) $(word 2,$(pair));)
$(if $($(PRIVATE_PICKUP_FILES)),$(hide) cp -Rf $(PRIVATE_PICKUP_FILES) $(PRIVATE_STAGING_DIR))
5、将描述镜像的信息存到image_info.txt。后面build_image.py构建会用
# Generate the dict.
$(hide) echo "# For all accepted properties, see BuildImage() in tools/releasetools/build_image.py" > $(PRIVATE_INTERMEDIATES)/image_info.txt
$(hide) echo "mount_point=$(PRIVATE_MOUNT_POINT)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt
........
$(hide) echo "# Properties from $(PRIVATE_DICT_FILE)" >> $(PRIVATE_INTERMEDIATES)/image_info.txt;\
cat $(PRIVATE_DICT_FILE) >> $(PRIVATE_INTERMEDIATES)/image_info.txt)
6、调用build_image.py来构建镜像,并传入3个参数
# Generate the image.
$(hide) PATH=$(foreach p,$(INTERNAL_USERIMAGES_BINARY_PATHS),$(p):)$$PATH \
./build/tools/releasetools/build_image.py \
$(PRIVATE_STAGING_DIR) $(PRIVATE_INTERMEDIATES)/image_info.txt $@
3. build_image.py解析入参,调用BuildImage()构建
# 构建image的输入源。代表着build system构建出的module存放的临时文件夹
in_dir = argv[0]
glob_dict_file = argv[1]
# 自定义的image
out_file = argv[2]
# 将image_info.txt转换成字典
glob_dict = LoadGlobalDict(glob_dict_file)
..............
# 调用BuildImage()
BuildImage(in_dir, image_properties, out_file, target_out)
4. BuildImage()构建出纯净镜像,调用avbtool进行签名
1. 创建builder,带AVB签名功能
verity_image_builder = verity_utils.CreateVerityImageBuilder(prop_dict)
2. 计算partition size
# 如果定义了use_dynamic_partition_size=true就动态就算partition
if (prop_dict.get("use_dynamic_partition_size") == "true" and "partition_size" not in prop_dict):
# erofs格式,先构建image,unsparse后再获取压缩后的erofs格式大小
if fs_type.startswith("erofs"):
mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
# 使用了sparse格式,需转回到erofs格式,再du获取大小
if "erofs_sparse_flag" in prop_dict and not disable_sparse:
image_path = UnsparseImage(out_file, replace=False)
size = GetDiskUsage(image_path)
os.remove(image_path)
else:
size = GetDiskUsage(out_file) # 没有使用sparse格式,直接du获取大小
else:
size = GetDiskUsage(in_dir) # 其他非压缩格式,使用du命令获取输入目录(需打包目录)的大小
........
# ext格式,构建image后,unsparse后得到ext,根据文件系统信息重新调整size
if fs_type.startswith("ext"):
prop_dict["partition_size"] = str(size)
prop_dict["image_size"] = str(size)
..........
BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
..........
# 构建image后,根据image的文件系统信息(比如inode),预留一些空间,重新调整size
..........
prop_dict["partition_size"] = str(size)
............
# f2fs格式处理方式和ext类似
elif fs_type.startswith("f2fs") and prop_dict.get("f2fs_compress") == "true":
prop_dict["partition_size"] = str(size)
prop_dict["image_size"] = str(size)
BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
............
prop_dict["partition_size"] = str(size)
# 为AVB继续调整size
if verity_image_builder:
size = verity_image_builder.CalculateDynamicPartitionSize(size)
prop_dict["partition_size"] = str(size)
prop_dict["image_size"] = prop_dict["partition_size"]
3. 计算image大小
# 将分区大小赋值给image
prop_dict["image_size"] = prop_dict["partition_size"]
# 调整image大小,给avb的hash留空间.
if verity_image_builder:
max_image_size = verity_image_builder.CalculateMaxImageSize()
prop_dict["image_size"] = str(max_image_size)
# 非erofs格式,根据最新的参数在此构建image
if not mkfs_output:
mkfs_output = BuildImageMkfs(in_dir, prop_dict, out_file, target_out, fs_config)
# 计算image大小,以适应挂载到分区以block形式存放的大小
prop_dict["image_size"] = common.sparse_img.GetImagePartitionSize(out_file)
...........
4. 调用avb签名流程
# AVB签名流程
if verity_image_builder:
verity_image_builder.Build(out_file)
5. avbtool签名流程
参考资料
链接: build_custom_images.mk
链接: build_custom_image.mk
链接: build_image.py
链接: Android 系统架构及HAL层概述Android 系统架构及HAL层概述
链接: Gitiles某个提交
链接: Android.mk的GNU make语法