本文将从下面三方面简单分析总结android property:
- Property的使用方式
- Property文件的加载
- Property的存储
1. Property的使用方式
在工作中经常通过下面三种方式使用property:
1.1 code里面使用SystemProperties.java和properties.cpp
SystemProperties.java为Jave层提供了下面的方法:
public static String get(String key){...}
public static String get(String key, String def){...}
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){...}
system/core/libcutils/properties.cpp给Native层提供了下面的API:
int property_set(const char *key, const char *value) {...}
int property_get(const char *key, char *value, const char *default_value) {...}
int8_t property_get_bool(const char *key, int8_t default_value) {...}
static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,
intmax_t default_value) {...}
int64_t property_get_int64(const char *key, int64_t default_value) {...}
int32_t property_get_int32(const char *key, int32_t default_value){...}
1.2 adb 命令
adb的方式为我们调式提供了方便。
[格式]
adb shell getprop [proptery_name]
adb shell setprop propterty_name value
[例子]
adb shell getprop ro.build.type
adb shell setprop persist.log.tag.ImsManager V
1.3 通过文件设置默认property;将property放在文件中,init进程去加载文件。
前两种方式可以读取所有的property;但是在写方面,对于ro.* property这种write-once的property是不能覆盖的。
2. Property文件的加载
Property的初始化,以及相关propety文件的加载都在Init进程中完成。所以这部分内容从init进程的main函数开始。按照main函数中的code顺序,主要内容如下:
- Property area初始化
- 加载default property文件
- 创建property service
- 加载解析rc文件
- 执行rc文件中的action; system property, persistent property和override property文件会在这个过程中相继被加载。
main函数会调用proprerty_service.cpp中的property_init()函数来初始化property area。
void property_init() {
if (__system_property_area_init()) {
LOG(ERROR) << "Failed to initialize property area";
exit(1);
}
}
property_init函数调用了System_properties.cpp中的__system_property_area_init()函数。后者又先后调用了initialize_properties()和map_system_property_area函数。
initialize_properties()函数:
initialize_properties会尝试加载"/property_contexts", 如果失败会尝试加载/system 和/vendor下对应的文件, 看来从Android O开始/property_contexts文件已经不再使用, 取而代之的是/plat_property_contexts。 "/property_contexts"等文件为property prefix分配了security context,用来控制set权限。
property_contexts内容如下:
...
sys.usb.config u:object_r:system_radio_prop:s0
ril. u:object_r:radio_prop:s0
ro.ril. u:object_r:radio_prop:s0
gsm. u:object_r:radio_prop:s0
...
static bool initialize_properties() {
// If we do find /property_contexts, then this is being
// run as part of the OTA updater on older release that had
// /property_contexts - b/34370523
if (initialize_properties_from_file("/property_contexts")) {//加载"/property_contexts"
return true;
}
// Use property_contexts from /system & /vendor, fall back to those from /
if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
//如果可以访问/system/etc/selinux/plat_property_contexts,就去加载解析文件。
if (!initialize_properties_from_file("/system/etc/selinux/plat_property_contexts")) {
return false;//这里如果解析失败就直接返回了,不再加载其他路径下的文件。
}
// Don't check for failure here, so we always have a sane list of properties.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
//这里去加载/vendor/etc/selinux/nonplat_property_contexts, 但是不care结果。
initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts");
} else {
//如果/system没有配置property_contexts文件,那么加载//plat_property_contexts
if (!initialize_properties_from_file("/plat_property_contexts")) {
return false;
}
initialize_properties_from_file("/nonplat_property_contexts");
}
return true;
}
/*initialize_properties_from_file负责解析property_contexts文件中的内容,
*将property前缀存在prefixes指向的链表中,将context存在contexts指向的链表中。
*/
static bool initialize_properties_from_file(const char* filename) {
FILE* file = fopen(filename, "re");
if (!file) {
return false;
}
char* buffer = nullptr;
size_t line_len;
char* prop_prefix = nullptr;
char* context = nullptr;
while (getline(&buffer, &line_len, file) > 0) {
int items = read_spec_entries(buffer, 2, &prop_prefix, &context);
if (items <= 0) {
continue;
}
if (items == 1) {
free(prop_prefix);
continue;
}
/*
* init uses ctl.* properties as an IPC mechanism and does not write them
* to a property file, therefore we do not need to create property files
* to store them.
*/
if (!strncmp(prop_prefix, "ctl.", 4)) {
free(prop_prefix);
free(context);
continue;
}
auto old_context =
list_find(contexts, [context](context_node* l) { return !strcmp(l->context(), context); });
if (old_context) {
list_add_after_len(&prefixes, prop_prefix, old_context);
} else {
list_add(&contexts, context, nullptr);
list_add_after_len(&prefixes, prop_prefix, contexts);
}
free(prop_prefix);
free(context);
}
free(buffer);
fclose(file);
return true;
}
map_system_property_area函数:
在看map_system_property_area(…)函数之前,我们先看下__system_property_area_init()函数。
int __system_property_area_init() {
free_and_unmap_contexts();
//property_filename所指的路径为/dev/__properties__; #define PROP_FILENAME "/dev/__properties__"
mkdir(property_filename, S_IRWXU | S_IXGRP | S_IXOTH);
if (!initialize_properties()) {
return -1;
}
bool open_failed = false;
bool fsetxattr_failed = false;
/*从property_contexts中解析的context全在contexts指向的链表中,下面这段代码就是遍历链表,
*open函数将每个context_node节点对应的文件都打开,并映射到一块share memory中。
*并在这块memory上构造一个prop_area指针存在context_node节点中。
*/
list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {
if (!l->open(true, &fsetxattr_failed)) {
open_failed = true;
}
});
if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {
free_and_unmap_contexts();
return -1;
}
initialized = true;
return fsetxattr_failed ? -2 : 0;
}
下面看看map_system_property_area函数:
打开/dev/properties/properties_serial文件,并映射到共享内存,将地址保存在__system_property_area__中。
static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) {
char filename[PROP_FILENAME_MAX];
//经过下面的格式化之后filename指向的文件为/dev/__properties__/properties_serial
int len =
__libc_format_buffer(filename, sizeof(filename), "%s/properties_serial", property_filename);
if (len < 0 || len > PROP_FILENAME_MAX) {
__system_property_area__ = nullptr;
return false;
}
if (access_rw) {
/*map_prop_area_rw函数会打开filename,映射一块共享内存,然后将地址返回。
*地址将保存在 __system_property_area__中, 这个是一个全局变量,property
*相关的操作还会用到这个变量。
*/
__system_property_area__ =
map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
} else {
__system_property_area__ = map_prop_area(filename);
}
return __system_property_area__;
}
到这里property area初始化就完成了。
在讲property文件的加载之前,有必要先讲下rc文件相关的知识点。rc文件被加载解析之后,所有的action会被放到ActionManager的actions_ (vector类型)容器里; 而action对应的commend对放到对应action的commands_ (vector类型)容器里。ActionManager负责根据trigger来执行action。
command对象在被创建的时候,会根据关键字在KeywordMap类型的指针function_map_ 中查找对应的function; function_map_ 的赋值是在main函数中,使用的是BuiltinFunctionMap对象,BuiltinFunctionMap是继承自KeywordMap。最终是在BuiltinFunctionMap内Map类型的变量builtin_functions中查找。
下面是buildin_functions中的部分内容:
{"load_persist_props", {0, 0, do_load_persist_props}},
{"load_system_props", {0, 0, do_load_system_props}},
{"loglevel", {1, 1, do_loglevel}},
下面是init.rc中的相关内容:
on post-fs
# Load properties from
# /system/build.prop,
# /odm/build.prop,
# /vendor/build.prop and
# /factory/factory.prop
load_system_props
...
on load_persist_props_action
load_persist_props
start logd
start logd-reinit
...
on property:vold.decrypt=trigger_load_persist_props
load_persist_props
start logd
当load_persist_props和load_system_props 命令执行的时候do_load_persist_props和do_load_system_props 函数会分别执行。
//下面是system property
static int do_load_system_props(const std::vector<std::string>& args) {
load_system_props();
return 0;
}
void load_system_props() {
load_properties_from_file("/system/build.prop", NULL);
load_properties_from_file("/odm/build.prop", NULL);
load_properties_from_file("/vendor/build.prop", NULL);
load_properties_from_file("/factory/factory.prop", "ro.*");
load_recovery_id_prop();
}
//下面是persist properties
static int do_load_persist_props(const std::vector<std::string>& args) {
load_persist_props();
return 0;
}
void load_persist_props(void) {
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
property_set("ro.persistent_properties.ready", "true");
}
static void load_override_properties() {
if (ALLOW_LOCAL_PROP_OVERRIDE) {
load_properties_from_file("/data/local.prop", NULL);
}
}
在执行rc文件内的action之前,main函数会调用property_load_boot_defaults()函数加载默认property; 调用start_property_service()函数来创建了一个名字为property_service的Unix domain Socket(PROP_SERVICE_NAME:property_service)来处理set prop请求。
void property_load_boot_defaults() {
if (!load_properties_from_file("/system/etc/prop.default", NULL)) {
// Try recovery path
if (!load_properties_from_file("/prop.default", NULL)) {
// Try legacy path
load_properties_from_file("/default.prop", NULL);
}
}
load_properties_from_file("/odm/default.prop", NULL);
load_properties_from_file("/vendor/default.prop", NULL);
update_sys_usb_config();
}
下面总结了可能会被加载的property文件或property(按照code顺序):
File/property | comments |
---|---|
/system/etc/prop.default | |
/prop.default | |
/default.prop | |
/odm/default.prop | |
/vendor/default.prop | |
/system/build.prop | |
/odm/build.prop | |
/vendor/build.prop | |
/factory/factory.prop | 只加载ro.* |
ro.recovery_id | |
/data/local.prop | |
/data/property | 这个路径下只加载persist开头的property |
/system/build.prop, /vendor/build.prop(PRODUCT_PROPERTY_OVERRIDES包含在内),/default.prop等生成规则都在代码build/core/Makefile中有定义,当然如果不懂make 语法和函数,是不可能看的很明白。 |
3. Property的存储
Property的存储,工作中用不到,所以不想深究,根据System_properties.cpp中的注释知道使用的是混合树结构(hybrid trie/binary tree),查找速度快,也省空间(分割了前缀, 可以共用)。
Property是只能由init进程(单线程)更新, 由property service完成。为了避免对线程读的问题,在节点上使用了atomic_uint_least32_t。
/*
* Properties are stored in a hybrid trie/binary tree structure.
* Each property's name is delimited at '.' characters, and the tokens are put
* into a trie structure. Siblings at each level of the trie are stored in a
* binary tree. For instance, "ro.secure"="1" could be stored as follows:
*
* +-----+ children +----+ children +--------+
* | |-------------->| ro |-------------->| secure |
* +-----+ +----+ +--------+
* / \ / |
* left / \ right left / | prop +===========+
* v v v +-------->| ro.secure |
* +-----+ +-----+ +-----+ +-----------+
* | net | | sys | | com | | 1 |
* +-----+ +-----+ +-----+ +===========+
*/