更多干货,请关注微信公众号: tmac_lover
上一篇文章向大家介绍Android Build System的lunch和mmm的原理,本文继续阐述Android系统编译时property生成原理。希望阅读完本文后大家懂得如何在自己制作的ROM中生成系统默认的property属性。本想将Android系统中property读写的实现机制一起写的,但是我还是坚持自己的观点,每篇文章不能太长 ,因为我自己没有耐心看一篇很长的技术文章,所以关于property读写机制的原理,准备放在下一篇推送。
另外,Adroid Build System将会做为一个系列长久延续下去,并在中间穿插分享一些Android其它知识。希望有兴趣的朋友持续关注
Android property是是Android系统中非常重要的一个机制,它类似于Windows系统中的注册表,每条属性都是key/value形式的键值对。property里保存了很多系统相关的重要信息,比如dalvik.vm.heapsize之类的。
使用property的方式有很多:
$ getprop
$ setprop dalvik.vm.heapsize value
#include "cutils/properties.h"
property_set("dalvik.vm.heapsize", "256m");
char value[32];
property_get("dalvik.vm.heapsize", value, "");
import android.os.SystemProperties;
SystemProperties.set("dalvik.vm.heapsize","256m");
String value = SystemProperties.get("dalvik.vm.heapsize", "");
当然,大部分property是在编译制作ROM的时候,由编译系统将预置的property放到image的指定文件中去,然后开机时,init自动加载这些文件,并提供proterty的读写机制。这篇文章就来介绍下Build System是如何预制property到自己编译的ROM里的。
上一篇推文中介绍了,lunch命令后,编译系统会根据目标平台的AndroidProducts.mk文件(一般在device/或vendor/目录下)找到所有需包含进行编译的makefile文件(一般是.mk文件),以我工作中接触的mstar平台代码为例,它们全在device/mstar/mangosteen/下,同时在这个目录下有很多.mk结尾的文件,认真分析它们之间include关系后会发现,这些全是lunch所选的平台编译所需要的。
在这些.mk文伯里,PRODUCT_PROPERTY_OVERRIDES,PRODUCT_DEFAULT_PROPERTY_OVERRIDES和PRODUCT_OEM_PROPERTIES就是和预置property相关的变量,通过设置这个变量,就能实现系统预置property,可以这样写:
PRODUCT_PROPERTY_OVERRIDES += persist.sys.country=CN
因为不同芯片厂商的差异性,可能您所看的Makefile的文件名字和写法会和我的有差异,但是原理肯定是一样的
那编译系统是如何来处理这个变量的呢?我们需要先来看看编译系统是如何处理property的。
PRODUCT_DEFAULT_PROPERTY_OVERRIDES变量的值通过build/core/Makefile文件中下面这段代码写到image镜像中/default.prop文件中。
[build/core/Makefile]
INSTALLED_DEFAULT_PROP_TARGET := $(TARGET_ROOT_OUT)/default.prop
ADDITIONAL_DEFAULT_PROPERTIES := \
$(call collapse-pairs, $(ADDITIONAL_DEFAULT_PROPERTIES))
ADDITIONAL_DEFAULT_PROPERTIES += \
$(call collapse-pairs, $(PRODUCT_DEFAULT_PROPERTY_OVERRIDES))
ADDITIONAL_DEFAULT_PROPERTIES := $(call uniq-pairs-by-first-component, \
$(ADDITIONAL_DEFAULT_PROPERTIES),=)
intermediate_system_build_prop := $(call intermediates-dir-for,ETC,system_build_prop)/build.prop
$(INSTALLED_DEFAULT_PROP_TARGET): $(intermediate_system_build_prop)
@echo Target buildinfo: $@
@mkdir -p $(dir $@)
$(hide) echo "#" > $@; \
echo "# ADDITIONAL_DEFAULT_PROPERTIES" >> $@; \
echo "#" >> $@;
$(hide) $(foreach line,$(ADDITIONAL_DEFAULT_PROPERTIES), \
echo "$(line)" >> $@;)
$(hide) echo "#" >> $@; \
echo "# BOOTIMAGE_BUILD_PROPERTIES" >> $@; \
echo "#" >> $@;
$(hide) echo ro.bootimage.build.date=`date`>>$@
$(hide) echo ro.bootimage.build.date.utc=`date +%s`>>$@
$(hide) echo ro.bootimage.build.fingerprint="$(BUILD_FINGERPRINT)">>$@
$(hide) build/tools/post_process_props.py $@
PRODUCT_DEFAULT_PROPERTY_OVERRIDES :=
$(strip $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_DEFAULT_PROPERTY_OVERRIDES))
后面这个变量看着熟悉吗,在上一篇里说过,device/mstar/mangosteen/目录中定义的变量会被赋值给PRODUCTS.$(INTERNAL_PRODUCT).xxx这种形式的变量,不明白原因的可以回过头去看看上篇文章。所以上面代码中变量PRODUCT_DEFAULT_PROPERTY_OVERRIDES和上文提到的device/mstar/mangosteen/目录下.mk文件中的PRODUCT_DEFAULT_PROPERTY_OVERRIDES变量,虽然名字一样,实际上的值也一样,但其实并不是同一个东西,注意这里的理解。
* 函数uniq-pairs-by-first-component定义在build/core/definitions.mk文件里,它的作用就是对ADDITIONAL_DEFAULT_PROPERTIES里的赋值语句去重,如果有发现对同一个property_name赋值多次,则只保留第一个值。
* 除了ADDITIONAL_DEFAULT_PROPERTIES里所有的property值以外,还会写入ro.bootimage.build.date这些,标识系统的编译时间
* 在最后会执行build/tools/post_process_props.py这个python脚本,它的作用是删除default.prop文件中指定的property(在函数的第二个参数中指定,没有则表明不指定);并且检查每个property名字和值的长度是否超过最大值,并且在这个python脚本中还可以根据需求再修改一次property值作为预置。
make命令执行完上面这段之后,文件out/target/product/${target_device}/root/default.prop就会创建成功,并且里面会有一些property值。
PRODUCT_PROPERTY_OVERRIDES和PRODUCT_DEFAULT_PROPERTY_OVERRIDES变量类似,不过它最终在被放到image镜像中的/system/build.prop文件中。我们来看看它的生成makefile。
[build/core/Makefile]
INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop
ADDITIONAL_BUILD_PROPERTIES := \
$(call collapse-pairs, $(ADDITIONAL_BUILD_PROPERTIES))
ADDITIONAL_BUILD_PROPERTIES := $(call uniq-pairs-by-first-component, \
$(ADDITIONAL_BUILD_PROPERTIES),=)
$(intermediate_system_build_prop): $(BUILDINFO_SH) $(INTERNAL_BUILD_ID_MAKEFILE) $(BUILD_SYSTEM)/version_defaults.mk $(system_prop_file) $(INSTALLED_ANDROID_INFO_TXT_TARGET)
$(hide) echo > $@
ifneq ($(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OEM_PROPERTIES),)
$(hide) echo "#" >> $@; \
echo "# PRODUCT_OEM_PROPERTIES" >> $@; \
echo "#" >> $@;
$(hide) $(foreach prop,$(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_OEM_PROPERTIES), \
echo "import /oem/oem.prop $(prop)" >> $@;)
endif
... ...
bash $(BUILDINFO_SH) >> $@
$(hide) $(foreach file,$(system_prop_file), \
if [ -f "$(file)" ]; then \
echo "#" >> $@; \
echo Target buildinfo from: "$(file)"; \
echo "# from $(file)" >> $@; \
echo "#" >> $@; \
cat $(file) >> $@; \
fi;)
$(if $(ADDITIONAL_BUILD_PROPERTIES), \
$(hide) echo >> $@; \
echo "#" >> $@; \
echo "# ADDITIONAL_BUILD_PROPERTIES" >> $@; \
echo "#" >> $@; )
$(hide) $(foreach line,$(ADDITIONAL_BUILD_PROPERTIES), \
echo "$(line)" >> $@;)
$(hide) build/tools/post_process_props.py $@ $(PRODUCTS.$(INTERNAL_PRODUCT).PRODUCT_SYSTEM_PROPERTY_BLACKLIST)
... ...
$(INSTALLED_BUILD_PROP_TARGET): $(intermediate_system_build_prop) $(INSTALLED_RECOVERYIMAGE_TARGET)
@echo "Target build info: $@"
$(hide) cat $(intermediate_system_build_prop) > $@
这段Makefile执行完成之后,build.prop文件就会在指定目录下生成,大概介绍一下生成过程:
Android系统中还定义了PRODUCT_OEM_PROPERTIES这个娈量,不过这个变量的使用方法和上面两个不一样;因为这个变量指定的property才会被init从/oem/oem.prop文件中load进来。它的使用方法如下:
PRODUCT_OEM_PROPERTIES := ro.config.ringtone
在2.2节中的代码里会发现,make的时候会遍历PRODUCT_OEM_PROPERTIES变量,并往build.prop中输入”import /oem/oem.prop $(prop)”, 这样init就会去/oem/oem.prop中找到指定的property属性对应的值,并加载到系统中。后面关于property属性读写实现会涉及到。
如果你只是想在你自己的ROM中添预置一些你的产品所需要的property,但是并不想了解Android系统中是如何实现property读写机制的,那看完这篇文章我相信你一定已经知道要怎么做了。不过在这里我还是要总结一下:
这篇文章只是介绍了Build System是如何往自己的ROM中定制property键值对的,在下一篇文章里,会仔细讲一下Android系统是如何实现property的读写机制的。