安卓property_service代码移植供app开发使用(上)
源码基于aosp_7.1()
位置/bionic/libc/bionic/system_properties.cpp
1: 先来看官方的注释:
/*
* 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 |
* +-----+ +-----+ +-----+ +===========+
*/
属性存储在一个混合的二叉树结构体中。每个属性名都会以 .
作为分隔符, 分割的子串会被放在一个trie结构体。
同级别的兄弟节点以二叉树的形式存放。比如ro.secure = 1
会被如上图中存储
2: 几个重要的结构体
2.1 prop_bt
Property-trie结构体仅仅会被init进程更新,因为init进程初始化属性服务并拥有绝对主权。 结构体可以同时被多个线程读取。但是结构体并没有线程保护锁,所以trie节点中的成员属性left、right、children等指针均使用atomic_uint_leaset32_t类型。
为了确保访问prop_bt的线程同时也能注意到left、right、children等具有亲密关系节点的变化,通常访问这些节点的时候使用release-consume序列
如果当前的trie节点有一个属性关联,那么prop指针就指向了一个prop_info节点。left、right、children指针也是如此。
这几个指针都使用了atomic_uint_least32_t,并且使用release-consume访问序列类型保护
struct prop_bt {
uint8_t namelen;
uint8_t reserved[3];
// The property trie is updated only by the init process (single threaded) which provides
// property service. And it can be read by multiple threads at the same time.
// As the property trie is not protected by locks, we use atomic_uint_least32_t types for the
// left, right, children "pointers" in the trie node. To make sure readers who see the
// change of "pointers" can also notice the change of prop_bt structure contents pointed by
// the "pointers", we always use release-consume ordering pair when accessing these "pointers".
// prop "points" to prop_info structure if there is a propery associated with the trie node.
// Its situation is similar to the left, right, children "pointers". So we use
// atomic_uint_least32_t and release-consume ordering to protect it as well.
// We should also avoid rereading these fields redundantly, since not
// all processor implementations ensure that multiple loads from the
// same field are carried out in the right order.
atomic_uint_least32_t prop;
atomic_uint_least32_t left;
atomic_uint_least32_t right;
atomic_uint_least32_t children;
char name[0];
prop_bt(const char *name, const uint8_t name_length) {
this->namelen = name_length;
memcpy(this->name, name, name_length);
this->name[name_length] = '\0';
}
private:
DISALLOW_COPY_AND_ASSIGN(prop_bt);
};
2.2 prop_info
该结构体被prop_bt使用,如果trie节点关联一个属性,那么该属性的详细信息就会以prop_info存储,包括value, name字符串。
struct prop_info {
atomic_uint_least32_t serial;
char value[PROP_VALUE_MAX];
char name[0];
prop_info(const char *name, const uint8_t namelen, const char *value,
const uint8_t valuelen) {
memcpy(this->name, name, namelen);
this->name[namelen] = '\0';
atomic_init(&this->serial, valuelen << 24);
memcpy(this->value, value, valuelen);
this->value[valuelen] = '\0';
}
private:
DISALLOW_COPY_AND_ASSIGN(prop_info);
};
2.3 prop_area
该结构体我省略了一些代码。
以下代码都是需要说明一下的,所以没删除
class prop_area {
prop_area(const uint32_t magic, const uint32_t version) :
magic_(magic), version_(version) {
atomic_init(&serial_, 0);
memset(reserved_, 0, sizeof(reserved_));
// Allocate enough space for the root node.
bytes_used_ = sizeof(prop_bt);
}
const prop_info *find(const char *name);
private:
void *allocate_obj(const size_t size, uint_least32_t *const off);
prop_bt *new_prop_bt(const char *name, uint8_t namelen, uint_least32_t *const off);
prop_info *new_prop_info(const char *name, uint8_t namelen,
const char *value, uint8_t valuelen,
uint_least32_t *const off);
void *to_prop_obj(uint_least32_t off);
prop_bt *to_prop_bt(atomic_uint_least32_t *off_p);
prop_info *to_prop_info(atomic_uint_least32_t *off_p);
prop_bt *root_node();
prop_bt *find_prop_bt(prop_bt *const bt, const char *name,
uint8_t namelen, bool alloc_if_needed);
const prop_info *find_property(prop_bt *const trie, const char *name,
uint8_t namelen, const char *value,
uint8_t valuelen, bool alloc_if_needed);
uint32_t bytes_used_;
atomic_uint_least32_t serial_;
uint32_t magic_;
uint32_t version_;
uint32_t reserved_[28];
char data_[0];
DISALLOW_COPY_AND_ASSIGN(prop_area);
}
Prop_area构造函数: serial_与同步有关系, bytes_used代表prop_area已使用空间,总空间是通过map属性文件的时候定义的
bytes_used_初始化为size_of(prop_bt),这段空间为root节点。
allocate_obj:在prop_area区域空间申请一段内存
new_prop_bt以及new_prop_info:内部都会调用allocate_obj
to_prop_obj: 将atomic_uint_least32_t转换为指针,其计算方法是通过data_ + offset偏移
void *prop_area::to_prop_obj(uint_least32_t off)
{
if (off > pa_data_size)
return NULL;
return (data_ + off);
}
root_node函数:data_ + offset,其中offset=0
find_prop_bt: 第一个参数为trie节点,第二个参数为name,表示需要查找的属性,比如ro.secure
Find_property: 类似find_prop_bt,其内部会调用find_prop_bt
3.prop_area初始化
3.1 init进程初始化property_service流程
3.1.1 /system/core/init/init.cpp中调用property_init()
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {
return ueventd_main(argc, argv);
}
if (!strcmp(basename(argv[0]), "watchdogd")) {
return watchdogd_main(argc, argv);
}
...............
...............
NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
if (!is_first_stage) {
// Indicate that booting is in progress to background fw loaders, etc.
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));
property_init();
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
process_kernel_dt();
process_kernel_cmdline();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();
}
3.1.2 /system/core/init/property_service.cpp中调用了system_property_area_init()
void property_init() {
if (__system_property_area_init()) {
ERROR("Failed to initialize property area\n");
exit(1);
}
}
3.1.3 /bionic/libc/bionic/system_properties.cpp中调用了context_node->open函数
int __system_property_area_init()
{
free_and_unmap_contexts();
mkdir(property_filename, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
if (!initialize_properties()) {
return -1;
}
bool open_failed = false;
bool fsetxattr_failed = false;
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;
}
3.1.4 /bionic/libc/bionic/system_properties.cpp中调用了map_prop_area_rw
bool context_node::open(bool access_rw, bool* fsetxattr_failed) {
lock_.lock();
if (pa_) {
lock_.unlock();
return true;
}
char filename[PROP_FILENAME_MAX];
int len = __libc_format_buffer(filename, sizeof(filename), "%s/%s",
property_filename, context_);
if (len < 0 || len > PROP_FILENAME_MAX) {
lock_.unlock();
return false;
}
if (access_rw) {
pa_ = map_prop_area_rw(filename, context_, fsetxattr_failed);
} else {
pa_ = map_prop_area(filename, false);
}
lock_.unlock();
return pa_;
}
3.1.5 解读一下map_prop_area_rw
- 以只读的方式open属性文件
- 通过fsetxattr设置文件的上下文
- 将属性文件的大小设置为PA_SIZE, PA_SIZE为宏(#define PA_SIZE (128 * 1024)), 128KB
- 将属性文件以可读写的模式共享到内存
- new (address) prop_area, 表示以address为内存地址,初始化prop_area结构体
static prop_area* map_prop_area_rw(const char* filename, const char* context,
bool* fsetxattr_failed) {
/* dev is a tmpfs that we can use to carve a shared workspace
* out of, so let's do that...
*/
const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
if (fd < 0) {
if (errno == EACCES) {
/* for consistency with the case where the process has already
* mapped the page in and segfaults when trying to write to it
*/
abort();
}
return nullptr;
}
if (context) {
if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0) {
__libc_format_log(ANDROID_LOG_ERROR, "libc",
"fsetxattr failed to set context (%s) for \"%s\"", context, filename);
/*
* fsetxattr() will fail during system properties tests due to selinux policy.
* We do not want to create a custom policy for the tester, so we will continue in
* this function but set a flag that an error has occurred.
* Init, which is the only daemon that should ever call this function will abort
* when this error occurs.
* Otherwise, the tester will ignore it and continue, albeit without any selinux
* property separation.
*/
if (fsetxattr_failed) {
*fsetxattr_failed = true;
}
}
}
if (ftruncate(fd, PA_SIZE) < 0) {
close(fd);
return nullptr;
}
pa_size = PA_SIZE;
pa_data_size = pa_size - sizeof(prop_area);
compat_mode = false;
void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (memory_area == MAP_FAILED) {
close(fd);
return nullptr;
}
prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
close(fd);
return pa;
}