Aosp 之 Property

1. Property 简介

Android 中有很多 Property,他们无处不在,我们的熟悉的 android.os.Build 中的很多字段都是直接读取的对应的 Property 值。Property 都是以键值对的形式存在,键和值都是字符串类型,他们是全局的(各个进程看到的都一样),Android 中非常多的进程和应用直接或者间接依赖于 Property 系统,并由此决定其运行期的行为。Property 实质上是由若干个属性读取进程和一个属性设置进程(Property Service)操作。

Aosp 之 Property

Property Service 位于 init 进程(拥有 root 权限),在系统开机后创建各个属性对应的内存映射文件。各个 App 进程直接 mmap 对应的内存映射文件,为上层的 property api 提供数据来源。

2. Property 模块的数据结构

属性共享内存中的内容,其实被组织成一棵字典树。内存块的第一个节点是个特殊的总述节点,类型为 prop_area。紧随其后的就是字典树的“树枝”和“树叶”了,树枝以 prop_bt 表达,树叶以 prop_info 表达。我们读取或设置属性值时,最终都只是在操作“叶子”节点而已。

Aosp 之 Property

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(&current->children, memory_order_relaxed);        if (children_offset != 0) {            root = to_prop_bt(&current->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(&current->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(&current->prop, memory_order_relaxed);    if (prop_offset != 0) {        return to_prop_info(&current->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(&current->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(&current->left, memory_order_relaxed);            if (left_offset != 0) {                current = to_prop_bt(&current->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(&current->left, new_offset, memory_order_release);                }                return new_bt;            }        } else {            uint_least32_t right_offset = atomic_load_explicit(&current->right, memory_order_relaxed);            if (right_offset != 0) {                current = to_prop_bt(&current->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(&current->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 服务。


  1. property_init();【system/core/init/init.c】 初始化属性共享内存

  2. property_load_boot_defaults();【system/core/init/init.c】加载属性文本文件

  3. property_service_init_action;【system/core/init/init.c】 初始化属性服务

  4. load_all_props; 【system/core/rootdir/init.rc】 加载属性文本文件

  5. queue_property_triggers_action;【system/core/init/init.c】 初始化属性后的触发动作

  6. 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。


上一篇:html5基础---h5特性


下一篇:CSS @property,让不可能变可能