Aosp 之 Property


葛泽续
2012 年 2 月加入去哪儿网,多次参与 Android 客户端的重构,插件化开发,客户端安全攻防,现负责国际酒店抓取系统架构升级维护。
1. Property 简介
Android 中有很多 Property,他们无处不在,我们的熟悉的 android.os.Build 中的很多字段都是直接读取的对应的 Property 值。Property 都是以键值对的形式存在,键和值都是字符串类型,他们是全局的(各个进程看到的都一样),Android 中非常多的进程和应用直接或者间接依赖于 Property 系统,并由此决定其运行期的行为。Property 实质上是由若干个属性读取进程和一个属性设置进程(Property Service)操作。

Property Service 位于 init 进程(拥有 root 权限),在系统开机后创建各个属性对应的内存映射文件。各个 App 进程直接 mmap 对应的内存映射文件,为上层的 property api 提供数据来源。
2. Property 模块的数据结构
属性共享内存中的内容,其实被组织成一棵字典树。内存块的第一个节点是个特殊的总述节点,类型为 prop_area。紧随其后的就是字典树的“树枝”和“树叶”了,树枝以 prop_bt 表达,树叶以 prop_info 表达。我们读取或设置属性值时,最终都只是在操作“叶子”节点而已。

prop_bt 定义:
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);
};
prop_area 定义:
class prop_area {
public:
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);
bool add(const char *name, unsigned int namelen,
const char *value, unsigned int valuelen);
bool foreach(void (*propfn)(const prop_info *pi, void *cookie), void *cookie);
atomic_uint_least32_t *serial() { return &serial_; }
uint32_t magic() const { return magic_; }
uint32_t version() const { return version_; }
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);
bool foreach_property(prop_bt *const trie,
void (*propfn)(const prop_info *pi, void *cookie),
void *cookie);
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_info 定义:
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);
};
每一个映射文件都是一个 128K 大小的二进制文件,以 prop_area 结构开头,它代表着共享内存块的起始,属性名将以‘.’符号为分割符,被分割开来。比如 ro.secure 属性名就会被分割成“ro”和“secure”两部分,而且每个部分用一个 prop_bt 节点表达。属性名中的这种‘.’关系被表示为父子关系,所以“ro”节点的 children 域,会指向“secure”节点。但是一个节点只有一个 children 域,如果它还有其他孩子,那些孩子将会和第一个子节点(比如 secure 节点)组成一棵二叉树。当一个属性名对应的“字典树枝”都已经形成好后,会另外创建一个 prop_info 节点,专门表示这个属性,该节点就是“字典树叶”。
在插入新的节点的时候是基于 strcmp() 的计算结果。插入字符串比原字符串大,会进一步和 right 节点比对,比原字符串小,会进一步和 left 节点对比,递归进行遍历对比,最后在合适的枝上建立新节点。
3. Property api 简介
一切从源码中找答案,不同的 Android 版本 api 稍有差异,这里以 7.1.1 为例讨论。常用 api 如下(下面的代码,只分析其中影响核心流程的部分):
__system_property_area_init 初始化属性内存映射文件,由 init 进程调用,其代码如下:
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;
}
这里主要做了如下工作:创建目录 /dev/__properties__初始化所有 context 打开 u:object_r:properties_serial:s0 这里会调用到 map_prop_area_rw,代码如下:
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;
}
通过以上代码可以看到对具体文件的 open 操作和 mmap 内存映射,同时还有 ftruncate 初始化文件大小为 PA_SIZE(128k)。
__system_properties_init 只读模式初始化属性,由 App 进程调用
int __system_properties_init()
{
if (initialized) {
list_foreach(contexts, [](context_node* l) { l->reset_access(); });
return 0;
}
if (is_dir(property_filename)) {
if (!initialize_properties()) {
return -1;
}
if (!map_system_property_area(false, nullptr)) {
free_and_unmap_contexts();
return -1;
}
} else {
__system_property_area__ = map_prop_area(property_filename, true);
if (!__system_property_area__) {
return -1;
}
list_add(&contexts, "legacy_system_prop_area", __system_property_area__);
list_add_after_len(&prefixes, "*", contexts);
}
initialized = true;
return 0;
}
这里调用 map_system_property_area 的时候传递参数是 false 表示以只读模式 mmap,与 init 进程不同的是,这里只是简单的调用 map_prop_area 进行文件映射。
static prop_area* map_prop_area(const char* filename, bool is_legacy) {
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
bool close_fd = true;
if (fd == -1 && errno == ENOENT && is_legacy) {
/*
* For backwards compatibility, if the file doesn't
* exist, we use the environment to get the file descriptor.
* For security reasons, we only use this backup if the kernel
* returns ENOENT. We don't want to use the backup if the kernel
* returns other errors such as ENOMEM or ENFILE, since it
* might be possible for an external program to trigger this
* condition.
* Only do this for the legacy prop file, secured prop files
* do not have a backup
*/
fd = get_fd_from_env();
close_fd = false;
}
if (fd < 0) {
return nullptr;
}
prop_area* map_result = map_fd_ro(fd);
if (close_fd) {
close(fd);
}
return map_result;
}
__system_property_add 增加属性
int __system_property_add(const char *name, unsigned int namelen,
const char *value, unsigned int valuelen)
{
if (namelen >= PROP_NAME_MAX)
return -1;
if (valuelen >= PROP_VALUE_MAX)
return -1;
if (namelen < 1)
return -1;
if (!__system_property_area__) {
return -1;
}
prop_area* pa = get_prop_area_for_name(name);
if (!pa) {
__libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name);
return -1;
}
bool ret = pa->add(name, namelen, value, valuelen);
if (!ret)
return -1;
// There is only a single mutator, but we want to make sure that
// updates are visible to a reader waiting for the update.
atomic_store_explicit(
__system_property_area__->serial(),
atomic_load_explicit(__system_property_area__->serial(), memory_order_relaxed) + 1,
memory_order_release);
__futex_wake(__system_property_area__->serial(), INT32_MAX);
return 0;
}
先根据传入的属性名调用 get_prop_area_for_name 匹配到该属性对应的 prop_area(下一步具体到哪一个文件里面找),进一步调用其 add 函数:
bool prop_area::add(const char *name, unsigned int namelen,
const char *value, unsigned int valuelen) {
return find_property(root_node(), name, namelen, value, valuelen, true);
}
继续看 find_property:
const prop_info *prop_area::find_property(prop_bt *const trie, const char *name,
uint8_t namelen, const char *value, uint8_t valuelen,
bool alloc_if_needed)
{
if (!trie) return NULL;
const char *remaining_name = name;
prop_bt* current = trie;
while (true) {
const char *sep = strchr(remaining_name, '.');
const bool want_subtree = (sep != NULL);
const uint8_t substr_size = (want_subtree) ?
sep - remaining_name : strlen(remaining_name);
if (!substr_size) {
return NULL;
}
prop_bt* root = NULL;
uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed);
if (children_offset != 0) {
root = to_prop_bt(¤t->children);
} else if (alloc_if_needed) {
uint_least32_t new_offset;
root = new_prop_bt(remaining_name, substr_size, &new_offset);
if (root) {
atomic_store_explicit(¤t->children, new_offset, memory_order_release);
}
}
if (!root) {
return NULL;
}
current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
if (!current) {
return NULL;
}
if (!want_subtree)
break;
remaining_name = sep + 1;
}
uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed);
if (prop_offset != 0) {
return to_prop_info(¤t->prop);
} else if (alloc_if_needed) {
uint_least32_t new_offset;
prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset);
if (new_info) {
atomic_store_explicit(¤t->prop, new_offset, memory_order_release);
}
return new_info;
} else {
return NULL;
}
}
这里会对传入的 name,以 “.”分割匹配。
查找 prop_bt:
prop_bt *prop_area::find_prop_bt(prop_bt *const bt, const char *name,
uint8_t namelen, bool alloc_if_needed)
{
prop_bt* current = bt;
while (true) {
if (!current) {
return NULL;
}
const int ret = cmp_prop_name(name, namelen, current->name, current->namelen);
if (ret == 0) {
return current;
}
if (ret < 0) {
uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed);
if (left_offset != 0) {
current = to_prop_bt(¤t->left);
} else {
if (!alloc_if_needed) {
return NULL;
}
uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
atomic_store_explicit(¤t->left, new_offset, memory_order_release);
}
return new_bt;
}
} else {
uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed);
if (right_offset != 0) {
current = to_prop_bt(¤t->right);
} else {
if (!alloc_if_needed) {
return NULL;
}
uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
atomic_store_explicit(¤t->right, new_offset, memory_order_release);
}
return new_bt;
}
}
}
}
__system_property_update 修改属性
int __system_property_update(prop_info *pi, const char *value, unsigned int len)
{
if (len >= PROP_VALUE_MAX)
return -1;
prop_area* pa = __system_property_area__;
if (!pa) {
return -1;
}
uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
serial |= 1;
atomic_store_explicit(&pi->serial, serial, memory_order_relaxed);
// The memcpy call here also races. Again pretend it
// used memory_order_relaxed atomics, and use the analogous
// counterintuitive fence.
atomic_thread_fence(memory_order_release);
memcpy(pi->value, value, len + 1);
atomic_store_explicit(
&pi->serial,
(len << 24) | ((serial + 1) & 0xffffff),
memory_order_release);
__futex_wake(&pi->serial, INT32_MAX);
atomic_store_explicit(
pa->serial(),
atomic_load_explicit(pa->serial(), memory_order_relaxed) + 1,
memory_order_release);
__futex_wake(pa->serial(), INT32_MAX);
return 0;
}
属性的修改非常简单,只是一个 memcpy 操作,但是这里只有 init 进程才有权限执行修改操作,app 进程若想要修改需调用__system_property_set 函数。
__system_property_get 读取属性
int __system_property_get(const char *name, char *value)
{
const prop_info *pi = __system_property_find(name);
if (pi != 0) {
return __system_property_read(pi, 0, value);
} else {
value[0] = 0;
return 0;
}
}
这里的 get 分两步:
第一步获取对应的 prop_info(__system_property_find),
第二步读取其中的 value 值(__system_property_read):
const prop_info *__system_property_find(const char *name)
{
if (!__system_property_area__) {
return nullptr;
}
if (__predict_false(compat_mode)) {
return __system_property_find_compat(name);
}
prop_area* pa = get_prop_area_for_name(name);
if (!pa) {
__libc_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name);
return nullptr;
}
return pa->find(name);
}
const prop_info *prop_area::find(const char *name) {
return find_property(root_node(), name, strlen(name), nullptr, 0, false);
}
再来看 read:
int __system_property_read(const prop_info *pi, char *name, char *value)
{
if (__predict_false(compat_mode)) {
return __system_property_read_compat(pi, name, value);
}
while (true) {
uint32_t serial = __system_property_serial(pi); // acquire semantics
size_t len = SERIAL_VALUE_LEN(serial);
memcpy(value, pi->value, len + 1);
// TODO: Fix the synchronization scheme here.
// There is no fully supported way to implement this kind
// of synchronization in C++11, since the memcpy races with
// updates to pi, and the data being accessed is not atomic.
// The following fence is unintuitive, but would be the
// correct one if memcpy used memory_order_relaxed atomic accesses.
// In practice it seems unlikely that the generated code would
// would be any different, so this should be OK.
atomic_thread_fence(memory_order_acquire);
if (serial ==
load_const_atomic(&(pi->serial), memory_order_relaxed)) {
if (name != 0) {
strcpy(name, pi->name);
}
return len;
}
}
}
__system_property_read 实质上只是简单的进行 value 的 memcpy。
__system_property_set 设置属性
这个 api 是给 App 进程使用的,会通过 socket 转发给 Property Service(init 进程)进行真正的修改操作。
int __system_property_set(const char *key, const char *value)
{
if (key == 0) return -1;
if (value == 0) value = "";
if (strlen(key) >= PROP_NAME_MAX) return -1;
if (strlen(value) >= PROP_VALUE_MAX) return -1;
prop_msg msg;
memset(&msg, 0, sizeof msg);
msg.cmd = PROP_MSG_SETPROP;
strlcpy(msg.name, key, sizeof msg.name);
strlcpy(msg.value, value, sizeof msg.value);
const int err = send_prop_msg(&msg);
if (err < 0) {
return err;
}
return 0;
}
将 key 和 value 组装成 prop_msg 调用 send_prop_msg 发送给远端进程修改。
static int send_prop_msg(const prop_msg *msg)
{
const int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);
if (fd == -1) {
return -1;
}
const size_t namelen = strlen(property_service_socket);
sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
addr.sun_family = AF_LOCAL;
socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
if (TEMP_FAILURE_RETRY(connect(fd, reinterpret_cast<sockaddr*>(&addr), alen)) < 0) {
close(fd);
return -1;
}
const int num_bytes = TEMP_FAILURE_RETRY(send(fd, msg, sizeof(prop_msg), 0));
int result = -1;
if (num_bytes == sizeof(prop_msg)) {
// We successfully wrote to the property server but now we
// wait for the property server to finish its work. It
// acknowledges its completion by closing the socket so we
// poll here (on nothing), waiting for the socket to close.
// If you 'adb shell setprop foo bar' you'll see the POLLHUP
// once the socket closes. Out of paranoia we cap our poll
// at 250 ms.
pollfd pollfds[1];
pollfds[0].fd = fd;
pollfds[0].events = 0;
const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));
if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) {
result = 0;
} else {
// Ignore the timeout and treat it like a success anyway.
// The init process is single-threaded and its property
// service is sometimes slow to respond (perhaps it's off
// starting a child process or something) and thus this
// times out and the caller thinks it failed, even though
// it's still getting around to it. So we fake it here,
// mostly for ctl.* properties, but we do try and wait 250
// ms so callers who do read-after-write can reliably see
// what they've written. Most of the time.
// TODO: fix the system properties design.
result = 0;
}
}
close(fd);
return result;
}
建立 socket 连接进行跨进程通信。
__system_property_foreach 遍历所有属性
int __system_property_foreach(void (*propfn)(const prop_info *pi, void *cookie),
void *cookie)
{
if (!__system_property_area__) {
return -1;
}
if (__predict_false(compat_mode)) {
return __system_property_foreach_compat(propfn, cookie);
}
list_foreach(contexts, [propfn, cookie](context_node* l) {
if (l->check_access_and_open()) {
l->pa()->foreach(propfn, cookie);
}
});
return 0;
}
分别调用每个 prop_area 的 foreach 进行遍历:
bool prop_area::foreach(void (*propfn)(const prop_info* pi, void* cookie), void* cookie) {
return foreach_property(root_node(), propfn, cookie);
}
真正的 foreach 操作:
bool prop_area::foreach_property(prop_bt *const trie,
void (*propfn)(const prop_info *pi, void *cookie), void *cookie)
{
if (!trie)
return false;
uint_least32_t left_offset = atomic_load_explicit(&trie->left, memory_order_relaxed);
if (left_offset != 0) {
const int err = foreach_property(to_prop_bt(&trie->left), propfn, cookie);
if (err < 0)
return false;
}
uint_least32_t prop_offset = atomic_load_explicit(&trie->prop, memory_order_relaxed);
if (prop_offset != 0) {
prop_info *info = to_prop_info(&trie->prop);
if (!info)
return false;
propfn(info, cookie);
}
uint_least32_t children_offset = atomic_load_explicit(&trie->children, memory_order_relaxed);
if (children_offset != 0) {
const int err = foreach_property(to_prop_bt(&trie->children), propfn, cookie);
if (err < 0)
return false;
}
uint_least32_t right_offset = atomic_load_explicit(&trie->right, memory_order_relaxed);
if (right_offset != 0) {
const int err = foreach_property(to_prop_bt(&trie->right), propfn, cookie);
if (err < 0)
return false;
}
return true;
}
以上对 prop_pt 的遍历实际上是二叉树的中序遍历,将遍历到的 prop_info 节点回调出去。
4. Property Service 的启动过程
Property Service 实体是运行在 init 进程,init 是 Linux 中运行的第一个进程拥有 root 权限,它负责创建系统中最关键的几个子进程(如:zygote)。在 init 进程初始化其他服务的同时,尽量早地在启动 property 服务。
property_init();【system/core/init/init.c】 初始化属性共享内存
property_load_boot_defaults();【system/core/init/init.c】加载属性文本文件
property_service_init_action;【system/core/init/init.c】 初始化属性服务
load_all_props; 【system/core/rootdir/init.rc】 加载属性文本文件
queue_property_triggers_action;【system/core/init/init.c】 初始化属性后的触发动作
handle_property_set_fd();【system/core/init/init.c】处理“ctl.”命令
对于 property 模块而言,启动完成后,就是在一个死循环中,不停地检查是否有他服务设置属性,如果有的话,接收和处理设置属性的请求。
在 property_init 过程中会调用到属性的添加 api,构建出对应属性的文件结构(其内容是二叉树状数据结构组织的各个树形节点)。
void property_init() {
if (__system_property_area_init()) {
ERROR("Failed to initialize property area\n");
exit(1);
}
}
5. Property 在 App 进程中的启动过程
上面提到了内存映射文件的初始化,那么在 App 进程里面具体是什么时机将文件 map 到内存里面的?这里利用 libc 的__libc_init 函数,当一个用户进程被调用起来时,内核会先调用到 C 运行期库(crtbegin)层次来初始化运行期环境,在这个阶段就会调用到__libc_init(),而后才会间接调用到 C 程序员熟悉的 main() 函数。可见属性共享内存在执行 main() 函数之前就已经映射好了。
一层层跟踪源码会发现,最终会调用到 __system_properties_init 完成属性的初始化。
void __libc_init_common(KernelArgumentBlock& args) {
// Initialize various globals.
environ = args.envp;
errno = 0;
__progname = args.argv[0] ? args.argv[0] : "<unknown>";
__abort_message_ptr = args.abort_message_ptr;
// Get the main thread from TLS and add it to the thread list.
pthread_internal_t* main_thread = __get_thread();
__pthread_internal_add(main_thread);
__system_properties_init(); // Requires 'environ'.
}
6. Java 层的封装
Java 层代码的封装相对简单,只是简单的通过 jni 调用 c 语言的函数,代码主要在 frameworks/base/core/java/android/os/SystemProperties.java 中。
public class SystemProperties {
private static final String TAG = "SystemProperties";
private static final boolean TRACK_KEY_ACCESS = false;
/**
* Android O removed the property name length limit, but com.amazon.kindle 7.8.1.5
* uses reflection to read this whenever text is selected (http://b/36095274).
*/
public static final int PROP_NAME_MAX = Integer.MAX_VALUE;
public static final int PROP_VALUE_MAX = 91;
private static final ArrayList<Runnable> sChangeCallbacks = new ArrayList<Runnable>();
@GuardedBy("sRoReads")
private static final HashMap<String, MutableInt> sRoReads;
static {
if (TRACK_KEY_ACCESS) {
sRoReads = new HashMap<>();
} else {
sRoReads = null;
}
}
private static void onKeyAccess(String key) {
if (!TRACK_KEY_ACCESS) return;
if (key != null && key.startsWith("ro.")) {
synchronized (sRoReads) {
MutableInt numReads = sRoReads.getOrDefault(key, null);
if (numReads == null) {
numReads = new MutableInt(0);
sRoReads.put(key, numReads);
}
numReads.value++;
if (numReads.value > 3) {
Log.d(TAG, "Repeated read (count=" + numReads.value
+ ") of a read-only system property '" + key + "'",
new Exception());
}
}
}
}
private static native String native_get(String key);
private static native String native_get(String key, String def);
private static native int native_get_int(String key, int def);
private static native long native_get_long(String key, long def);
private static native boolean native_get_boolean(String key, boolean def);
private static native void native_set(String key, String def);
private static native void native_add_change_callback();
private static native void native_report_sysprop_change();
/**
* Get the value for the given key.
* @return an empty string if the key isn't found
*/
public static String get(String key) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key);
}
/**
* Get the value for the given key.
* @return if the key isn't found, return def if it isn't null, or an empty string otherwise
*/
public static String get(String key, String def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key, def);
}
/**
* Get the value for the given key, and return as an integer.
* @param key the key to lookup
* @param def a default value to return
* @return the key parsed as an integer, or def if the key isn't found or
* cannot be parsed
*/
public static int getInt(String key, int def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_int(key, def);
}
/**
* Get the value for the given key, and return as a long.
* @param key the key to lookup
* @param def a default value to return
* @return the key parsed as a long, or def if the key isn't found or
* cannot be parsed
*/
public static long getLong(String key, long def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_long(key, def);
}
/**
* Get the value for the given key, returned as a boolean.
* Values 'n', 'no', '0', 'false' or 'off' are considered false.
* Values 'y', 'yes', '1', 'true' or 'on' are considered true.
* (case sensitive).
* If the key does not exist, or has any other value, then the default
* result is returned.
* @param key the key to lookup
* @param def a default value to return
* @return the key parsed as a boolean, or def if the key isn't found or is
* not able to be parsed as a boolean.
*/
public static boolean getBoolean(String key, boolean def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get_boolean(key, def);
}
/**
* Set the value for the given key.
* @throws IllegalArgumentException if the value exceeds 92 characters
*/
public static void set(String key, String val) {
if (val != null && val.length() > PROP_VALUE_MAX) {
throw newValueTooLargeException(key, val);
}
if (TRACK_KEY_ACCESS) onKeyAccess(key);
native_set(key, val);
}
public static void addChangeCallback(Runnable callback) {
synchronized (sChangeCallbacks) {
if (sChangeCallbacks.size() == 0) {
native_add_change_callback();
}
sChangeCallbacks.add(callback);
}
}
static void callChangeCallbacks() {
synchronized (sChangeCallbacks) {
//Log.i("foo", "Calling " + sChangeCallbacks.size() + " change callbacks!");
if (sChangeCallbacks.size() == 0) {
return;
}
ArrayList<Runnable> callbacks = new ArrayList<Runnable>(sChangeCallbacks);
for (int i=0; i<callbacks.size(); i++) {
callbacks.get(i).run();
}
}
}
private static IllegalArgumentException newValueTooLargeException(String key, String value) {
return new IllegalArgumentException("value of system property '" + key + "' is longer than "
+ PROP_VALUE_MAX + " characters: " + value);
}
/*
* Notifies listeners that a system property has changed
*/
public static void reportSyspropChanged() {
native_report_sysprop_change();
}
}
以 get 成员函数为例,get()->native_get()->System_Properties_getS()->property_get()->__system_property_get()最终都是会调用到上面提到的那几个核心 api。

版权声明: 本文为 InfoQ 作者【Qunar技术沙龙】的原创文章。
原文链接:【http://xie.infoq.cn/article/875b219a59fb30d3aabe625c2】。未经作者许可,禁止转载。

Qunar技术沙龙
还未添加个人签名 2020.11.28 加入
还未添加个人简介
评论