那么在Android平台上,也有类似的机制,称之为属性服务(property service)。应用程序可以通过这个属性机制,查询或者设置相应的属性。我们可以使用getprop命令来查看当前系统中都有哪些属性。比如我的红米手机,如图1所示(图中只显示了部分属性)。
图1
创建属性内容存储文件
属性服务的初始化工作主要是在init进程的main函数中完成。代码路径system\core\init\init.c。init的main函数中首先调用property_init()函数创建存储文件,代码如下:
- void property_init(void)
- {
- init_property_area();
- }
-
- static int init_property_area(void)
- {
-
- if (property_area_inited)
- return -1;
-
-
- if(__system_property_area_init())
- return -1;
-
-
-
-
-
-
-
-
-
- if(init_workspace(&pa_workspace, 0))
- return -1;
-
- fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
-
-
- property_area_inited = 1;
- return 0;
- }
再来看看__system_property_area_init函数具体做了什么事情,__system_property_area_init->map_prop_area_rw:
- static int map_prop_area_rw()
- {
- prop_area *pa;
- int fd;
- int ret;
-
-
-
-
-
-
-
-
- fd = open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC |
- O_EXCL, 0444);
- if (fd < 0) {
- if (errno == EACCES) {
-
-
-
- abort();
- }
- return -1;
- }
-
- ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
- if (ret < 0)
- goto out;
-
-
-
-
- if (ftruncate(fd, PA_SIZE) < 0)
- goto out;
-
-
-
-
-
-
-
-
-
-
-
-
- pa_size = PA_SIZE;
- pa_data_size = pa_size - sizeof(prop_area);
- compat_mode = false;
-
-
-
-
-
- pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if(pa == MAP_FAILED)
- goto out;
-
- memset(pa, 0, pa_size);
- pa->magic = PROP_AREA_MAGIC;
- pa->version = PROP_AREA_VERSION;
-
- pa->bytes_used = sizeof(prop_bt);
-
-
-
-
-
- __system_property_area__ = pa;
-
- close(fd);
- return 0;
-
- out:
- close(fd);
- return -1;
- }
上面的内容比较简单,不过最后的一个赋值语句__system_property_area__ = pa;比较特殊。__system_property_area__变量是在bioniclibc库中定义的一个全局变量。这里为什么要赋值给它呢?
原来,虽然属性区域是由init进程在初始化的时候创建的,但是Android希望其他进程也能读取这块内存里面的东西,为了做到这一点,它便做了如下两项工作:
1. 属性区域通过文件形式实现进程间共享数据。
2. 如何让其他进程也去读取这个文件呢?Android利用了gcc的constructor属性,这个属性指明了一个__libc_prenit函数,当bionic libc库被加载时,将自动调用这个__libc_prenit,这个函数内部就将完成共享文件到本地进程的映射工作。
客户端进程映射属性文件
直接看代码吧,bionic\libc\bionic\libc_init_dynamic.cpp
-
-
- __attribute__((constructor)) static void __libc_preinit() {
- …
- __libc_init_common(*args);
- …
- }
-
- void __libc_init_common(KernelArgumentBlock& args) {
- …
- __system_properties_init();
- }
-
- __system_properties_init –> map_prop_area
- static int map_prop_area()
- {
- bool fromFile = true;
- int result = -1;
- int fd;
- int ret;
-
-
- fd = open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- if (fd >= 0) {
-
- ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
- if (ret < 0)
- goto cleanup;
- }
-
- if ((fd < 0) && (errno == ENOENT)) {
- fd = get_fd_from_env();
- fromFile = false;
- }
-
- if (fd < 0) {
- return -1;
- }
-
-
- struct stat fd_stat;
- if (fstat(fd, &fd_stat) < 0) {
- goto cleanup;
- }
-
- if ((fd_stat.st_uid != 0)
- || (fd_stat.st_gid != 0)
- || ((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0)
- || (fd_stat.st_size < sizeof(prop_area)) ) {
- goto cleanup;
- }
-
-
- pa_size = fd_stat.st_size;
- pa_data_size = pa_size - sizeof(prop_area);
-
-
- prop_area *pa = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0);
-
- if (pa == MAP_FAILED) {
- goto cleanup;
- }
-
-
- if((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION &&
- pa->version != PROP_AREA_VERSION_COMPAT)) {
- munmap(pa, pa_size);
- goto cleanup;
- }
-
- if (pa->version == PROP_AREA_VERSION_COMPAT) {
- compat_mode = true;
- }
-
- result = 0;
-
-
- __system_property_area__ = pa;
-
- cleanup:
- if (fromFile) {
- close(fd);
- }
-
- return result;
- }
加载属性文件并启动属性服务
接着看init进程的初始化代码
-
- if (!is_charger)
- property_load_boot_defaults();
最后调用queue_builtin_action(property_service_init_action,"property_service_init");完成属性服务器的启动过程。
property_service_init_action-> start_property_service:
- void start_property_service(void)
- {
- int fd;
-
-
-
-
-
- load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
- load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
-
-
-
- load_override_properties();
-
-
-
-
-
- load_persistent_properties();
-
-
- fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
- if(fd < 0) return;
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fcntl(fd, F_SETFL, O_NONBLOCK);
-
-
- listen(fd, 8);
-
-
- property_set_fd = fd;
- }
init进程处理设置属性请求
设置属性的请求都是在init进程的main函数的一个for循环中进行处理的。下面我们继续看代码:
- for (i = 0; i < fd_count; i++) {
- if (ufds[i].revents == POLLIN) {
- if (ufds[i].fd == get_property_set_fd())
- handle_property_set_fd();
- else if (ufds[i].fd == get_keychord_fd())
- handle_keychord();
- else if (ufds[i].fd == get_signal_fd())
- handle_signal();
- }
- }
上面start_property_service函数中创建了一个用于通信的socket,最后赋值给全局变量property_set_fd,此处就是判断收到的信息是否是是属性服务器的socket。
- int get_property_set_fd()
- {
- return property_set_fd;
- }
如果是属性服务器的通信socket,那么就调用handle_property_set_fd函数来处理消息,并设置相应的属性。
- void handle_property_set_fd()
- {
- prop_msg msg;
- int s;
- int r;
- int res;
- struct ucred cr;
- struct sockaddr_un addr;
- socklen_t addr_size = sizeof(addr);
- socklen_t cr_size = sizeof(cr);
- char * source_ctx = NULL;
-
-
- if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
- return;
- }
-
-
-
-
-
- if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
- close(s);
- ERROR("Unable to receive socket options\n");
- return;
- }
-
-
- r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));
- if(r != sizeof(prop_msg)) {
- ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n",
- r, sizeof(prop_msg), errno);
- close(s);
- return;
- }
-
- switch(msg.cmd) {
- case PROP_MSG_SETPROP:
- msg.name[PROP_NAME_MAX-1] = 0;
- msg.value[PROP_VALUE_MAX-1] = 0;
-
-
- if (!is_legal_property_name(msg.name, strlen(msg.name))) {
- ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
- close(s);
- return;
- }
-
- getpeercon(s, &source_ctx);
-
-
-
-
- if(memcmp(msg.name,"ctl.",4) == 0) {
- close(s);
- if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) {
- handle_control_message((char*) msg.name + 4, (char*) msg.value);
- } else {
- ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
- msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
- }
- } else {
-
-
-
- if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) {
-
- property_set((char*) msg.name, (char*) msg.value);
- } else {
- ERROR("sys_prop: permission denied uid:%d name:%s\n",
- cr.uid, msg.name);
- }
-
-
-
-
- close(s);
- }
- freecon(source_ctx);
- break;
-
- default:
- close(s);
- break;
- }
- }
当客户端的权限满足要求时,init进程就调用property_set进行相关处理,这个函数逻辑比较简单,代码如下所示:
- int property_set(const char *name, const char *value)
- {
- prop_info *pi;
- int ret;
-
- size_t namelen = strlen(name);
- size_t valuelen = strlen(value);
-
-
- if (!is_legal_property_name(name, namelen)) return -1;
- if (valuelen >= PROP_VALUE_MAX) return -1;
-
-
- pi = (prop_info*) __system_property_find(name);
- if(pi != 0) {
-
- if(!strncmp(name, "ro.", 3)) return -1;
-
-
- __system_property_update(pi, value, valuelen);
- } else {
-
- ret = __system_property_add(name, namelen, value, valuelen);
- if (ret < 0) {
- ERROR("Failed to set '%s'='%s'\n", name, value);
- return ret;
- }
- }
-
- if (strncmp("net.", name, strlen("net.")) == 0) {
- if (strcmp("net.change", name) == 0) {
- return 0;
- }
-
-
-
-
-
- property_set("net.change", name);
- } else if (persistent_properties_loaded &&
- strncmp("persist.", name, strlen("persist.")) == 0) {
-
-
- write_persistent_property(name, value);
- } else if (strcmp("selinux.reload_policy", name) == 0 &&
- strcmp("1", value) == 0) {
-
- selinux_reload_policy();
- }
-
-
-
-
-
-
-
- property_changed(name, value);
- return 0;
- }
property_changed函数其主要实现如下,property_changed-> queue_property_triggers:
- void queue_property_triggers(const char *name, const char *value)
- {
- struct listnode *node;
- struct action *act;
- list_for_each(node, &action_list) {
- act = node_to_item(node, struct action, alist);
- if (!strncmp(act->name, "property:", strlen("property:"))) {
- const char *test = act->name + strlen("property:");
- int name_length = strlen(name);
-
- if (!strncmp(name, test, name_length) &&
- test[name_length] == '=' &&
- (!strcmp(test + name_length + 1, value) ||
- !strcmp(test + name_length + 1, "*"))) {
- action_add_queue_tail(act);
- }
- }
- }
- }
客户端设置属性
客户端通过property_set发送设置属性的请求,代码如下所示:property_set -> __system_property_set
- int __system_property_set(const char *key, const char *value)
- {
- int err;
- prop_msg msg;
-
- 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;
-
- memset(&msg, 0, sizeof msg);
- msg.cmd = PROP_MSG_SETPROP;
- strlcpy(msg.name, key, sizeof msg.name);
- strlcpy(msg.value, value, sizeof msg.value);
-
- err = send_prop_msg(&msg);
- if(err < 0) {
- return err;
- }
-
- return 0;
- }
-
- static int send_prop_msg(prop_msg *msg)
- {
- struct pollfd pollfds[1];
- struct sockaddr_un addr;
- socklen_t alen;
- size_t namelen;
- int s;
- int r;
- int result = -1;
-
- s = socket(AF_LOCAL, SOCK_STREAM, 0);
- if(s < 0) {
- return result;
- }
-
- memset(&addr, 0, sizeof(addr));
-
-
-
- namelen = strlen(property_service_socket);
- strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path);
- addr.sun_family = AF_LOCAL;
- alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;
-
-
- if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) {
- close(s);
- return result;
- }
-
- r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));
-
- if(r == sizeof(prop_msg)) {
- pollfds[0].fd = s;
- pollfds[0].events = 0;
- r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 ));
- if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) {
- result = 0;
- } else {
- result = 0;
- }
- }
-
- close(s);
- return result;
- }