上一篇文章向大家介绍Android Build System的lunch和mmm的原理,本文继续阐述Android系统编译时property生成原理。希望阅读完本文后大家懂得如何在自己制作的ROM中生成系统默认的property属性。本想将Android系统中property读写的实现机制一起写的,但是我还是坚持自己的观点,每篇文章不能太长 ,因为我自己没有耐心看一篇很长的技术文章,所以关于property读写机制的原理,准备放在下一篇推送。

另外,Adroid Build System将会做为一个系列长久延续下去,并在中间穿插分享一些Android其它知识。希望有兴趣的朋友持续关注

1. property介绍

Android property是是Android系统中非常重要的一个机制,它类似于Windows系统中的注册表,每条属性都是key/value形式的键值对。property里保存了很多系统相关的重要信息,比如dalvik.vm.heapsize之类的。

使用property的方式有很多:

  1. console里:
$ getprop
$ setprop dalvik.vm.heapsize value
  1. c/cpp代码:
#include "cutils/properties.h"

property_set("dalvik.vm.heapsize", "256m");

char value[32];
property_get("dalvik.vm.heapsize", value, "");
  1. java代码:
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里的。

2. 预置property

上一篇推文中介绍了,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的。

2.1 PRODUCT_DEFAULT_PROPERTY_OVERRIDES

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 $@
  • TARGET_ROOT_OUT在这里就是out/target/product/mangosteen/root/default.prop, 运行起来之后,在开发板上运行起来就是/default.prop文件。
  • 函数collapse-pairs定义在build/core/definitions.mk文件里,它的作用是将ADDITIONAL_DEFAULT_PROPERTIES和PRODUCT_DEFAULT_PROPERTY_OVERRIDES两个变量里的所有赋值语句’=’两端的空格去掉。
  • 变量ADDITIONAL_DEFAULT_PROPERTIES在make的过程中,根据我们的编译参数赋一些默认的property值,比如: ro.zygote=zygote64_32; 具体有哪些值,可以自行分析makefile
  • 这里的变量PRODUCT_DEFAULT_PROPERTY_OVERRIDES值是:

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值。

2.2 PRODUCT_PROPERTY_OVERRIDES

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文件就会在指定目录下生成,大概介绍一下生成过程:

  • system_prop_file变量代表的文件的内容会被复制到build.prop文件里去,system_prop_file代表的文件可以由TARGET_SYSTEM_PROP变量定义,也可以是device目录下的system.prop文件,在我这里是device/mstar/mangosteen/system.prop
  • BUILDINFO_SH = build/tools/buildinfo.sh脚本会输出很多property值给build.prop文件,这个脚本里很多值是从build/core/version_defaults.mk里获得的
  • ADDITIONAL_BUILD_PROPERTIES变量是build.prop文件内容的另一个主要来源之一,它的值由和上面的PRODUCT_DEFAULT_PROPERTY_OVERRIDES一样,由两部分组成,一部分是make时Makefile根据编译变量直接赋的值;另一部分就是device/mstar/mangosteen/目录下.mk文件中的
    PRODUCT_PROPERTY_OVERRIDES变量
  • build.prop文件会先被放到out/target/product/${target_device}/obj/ETC/system_build_prop_intermediates里,然后再一起拷贝到out/target/product/${target_device}/system/目录下

2.3 PRODUCT_OEM_PROPERTIES

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属性读写实现会涉及到。

3. 总结

如果你只是想在你自己的ROM中添预置一些你的产品所需要的property,但是并不想了解Android系统中是如何实现property读写机制的,那看完这篇文章我相信你一定已经知道要怎么做了。不过在这里我还是要总结一下:

  1. Android编译系统会将所有ROM里预置的property键值对放到/default.prop和/system/build.prop文件里
  2. make的时候,会根据lunch所选择的目标平台和编译选项设置一部分需要预置的property键值对
  3. 在自己ROM平台的.mk文件里,通过设置PRODUCT_PROPERTY_OVERRIDES,PRODUCT_DEFAULT_PROPERTY_OVERRIDES和PRODUCT_OEM_PROPERTIES这三个变量的值,可以添加我们自己ROM需要的property键值对

这篇文章只是介绍了Build System是如何往自己的ROM中定制property键值对的,在下一篇文章里,会仔细讲一下Android系统是如何实现property的读写机制的。