本文简要分析一下build.prop是如何生成的。以及Android属性系统的管理,获取及设置方法。
build.prop的生成
Android的build.prop文件是在Android编译时收集的各种property(LCD density/语言/编译时间, etc.),编译完成之后,文件生成在out/target/product/XXXX/system/目录下。
(1)build.prop的生成是由make系统解析build/core/Makefile完成。Makefile中首先定义各种变量,这在下一步执行时会用到。比如:
PRODUCT_DEFAULT_LANGUAGE="$(calldefault-locale-language,$(PRODUCT_LOCALES))" \
PRODUCT_DEFAULT_REGION="$(calldefault-locale-region,$(PRODUCT_LOCALES))" \
(2)Makefile中调用build/tools/buildinfo.sh执行脚本,并输出到build.prop。Buildinfo.sh很简单,只是echo一些属性,比如:
echo"ro.product.locale.language=$PRODUCT_DEFAULT_LANGUAGE"
echo"ro.product.locale.region=$PRODUCT_DEFAULT_REGION"
(3)Makefile中直接把$(TARGET_DEVICE_DIR)/system.prop的内容追加到build.prop中,还会收集ADDITIONAL_BUILD_PROPERTIES中的属性,追加到build.prop中。
ADDITIONAL_BUILD_PROPERTIES又会收集PRODUCT_PROPERTY_OVERRIDES中定义的属性,如下:
ADDITIONAL_BUILD_PROPERTIES:= \
$(ADDITIONAL_BUILD_PROPERTIES)\
$(PRODUCT_PROPERTY_OVERRIDES)
通过build.prop生成过程的分析,可以用以下方式修改原有的属性或加入自己定义属性:
- buildinfo.sh;
- system.prop;
- ADDITIONAL_BUILD_PROPERTIES或PRODUCT_PROPERTY_OVERRIDES。
属性的加载、读、设置
Android中属性从多个地方加载,加载的属性保存在/dev/__properties__
中,该文件由init进程创建并管理,其他进程只具备读的权限。当需要设置属性时,则通过socket通知到init进程设置。
属性的加载
首先init进程在启动的时候回进行属性系统的初始化:
property_init();
void property_init(void)
{
init_property_area();
}
init_property_area会创建/dev/__properties__内存文件,并通过mmap将其映射为一个内存指针。
其后,将会加载各种属性
- process_kernel_cmdline根据kernel及uboot环境变量,生成ro属性;
- property_load_boot_defaults函数,将从/default.prop加载部分属性到属性系统。
- init进程触发“property_service_init”action后,即调用property_service_init_action函数,该函数会加载大部分属性。如下:
static int property_service_init_action(int nargs, char **args)
{
/* read any property files on system or data and
* fire up the property service. This must happen
* after the ro.foo properties are set above so
* that /data/local.prop cannot interfere with them.
*/
start_property_service();
return 0;
}
start_property_service()会从这几个地方加载属性:
1)/system/build.prop文件中的属性
2)/system/default.prop文件中的属性
3)/data/property目录中读取persist打头的文件,读取的文件内容更新到文件名对应的属性中。
所以需要注意的是persist打头的属性,是可以保存的,而其他属性则是加载在内存中,不会保存。
读取,设置属性
相关函数:
C层
包含头文件:cutils/properties.h
需链接到动态库:libcutils.so
读写函数:
int property_get(const char *key, char *value, const char *default_value);
/* property_set: returns 0 on success, < 0 on failure
*/
int property_set(const char *key, const char *value);
Java层
类: android.os.SystemProperties
读
public static String get(String key);
public static int getInt(String key, int def);
public static long getLong(String key, long def);
public static boolean getBoolean(String key, boolean def);
写
public static void set(String key, String val) ;
读取属性
属性加载完成后,读取属性,无论是C层还是Java层,最终会调用到libc库中的函数,通共享内存的方式从/dev/__properties__
获取属性值。读取属性没有权限限制。
设置属性
init进程会创建并监听socket(/dev/socket/property_service)如果其他进程需要设置属性,则发送对应消息到该socket。然后再由init进程就设置属性。
设置属性有权限限制。
限制如下:
ctl开头的属性,需system或root用户,或者在control_perms中uid/gid匹配的用户。
非ctl开头的属性,需root用户,或者property_perms中uid/gid匹配的用户。
如:
property_perms[] = {
{ "net.dns", AID_RADIO, 0 },
{ "net.", AID_SYSTEM, 0 },
{ "dev.", AID_SYSTEM, 0 },
{ "runtime.", AID_SYSTEM, 0 },
{ "sys.", AID_SYSTEM, 0 },
{ "service.", AID_SYSTEM, 0 },
{ "persist.sys.", AID_SYSTEM, 0 },
{ "persist.service.", AID_SYSTEM, 0 },
……
{ NULL, 0, 0 }
};
属性触发器
init进程中,init.rc等rc文件中可以定义各种服务,属性触发器可以来控制系统服务的启动。
对于ctl开头的属性,可以设置其值,来控制init.rc中服务的启动、停止、重启。
如设置ctl.start为dhcpcd:eth0,则将启动服务dhcpcd,属性值中冒号后的内容作为服务参数。此外还有ctl.stop和ctl.restart属性,分别用于停止和重启服务。
另外我们可以在init.rc等rc文件中定义自己的属性触发器,当一个属性值等于XX时,触发下面的事件,比如启动一个进程,或者设置打印级别。
on property:sys.init_log_level=*
loglevel ${sys.init_log_level}