本文的大体流程还是按照书本上来,分三段。
(三)属性服务 (property_service)
由于本文内容较长,重新组织了下文章结构,将原文一分为三。
(三)属性服务 (property_service)
老实说,觉得自己讲不好这部分,建议读者参考《深入理解Android 卷1》 或者网上
其他文章。此处,我只是略提一下。
看到这个property,让我想起了注册表,也想起来以前工作中保存用户设置数据的部分。
涉及到了NAND Flash,ubifs等。当然,此处我们讲的这个property就不提那么多了。
1. 数据在NAND Flash里面,以便下次开机后能得到之前保存的数据。而进程访问
这些数据之前,有做mmap的动作,将数据映射到内存。
2.设置property,有C/S架构组成。客户端的程序位于properties.c,服务端的程序位于
property_service.c。
(1)Init.c的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; }
int __system_property_area_init() { return map_prop_area_rw(); }
static int map_prop_area_rw() { prop_area *pa; int fd; int ret; /* dev is a tmpfs that we can use to carve a shared workspace * out of, so let‘s do that... */ fd = open(property_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 -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; /* reserve root node */ pa->bytes_used = sizeof(prop_bt); /* plug into the lib property services */ __system_property_area__ = pa; close(fd); return 0; out: close(fd); return -1; }
和 pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
我们知道文件property_filename (即 /dev/__properties__)被打开,并以读写,可共享的方式
映射到了内存。(其他全局变量,如pa_data_size和__system_property_area__ 暂放置一边,
也不细节的讲述get_fd_from_env() )
static int init_workspace(workspace *w, size_t size) { void *data; int fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW); if (fd < 0) return -1; w->size = size; w->fd = fd; return 0; }
在main函数中接下来看到的是:property_load_boot_defaults
void property_load_boot_defaults(void) { load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT); }
static void load_properties_from_file(const char *fn) { char *data; unsigned sz; data = read_file(fn, &sz); if(data != 0) { load_properties(data); free(data); } }
反正就是将文件/default.pro 里面的properties 加载到内存。
(2) 接下来是:queue_builtin_action(property_service_init_action, "property_service_init");
和 queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
按照上面对service start的分析,我们知道property_service_init_action 和
queue_property_triggers_action 函数会被执行。
static int property_service_init_action(int nargs, char **args) { /* read any property files on system or data and * fire up the property service. This must happen * after the ro.foo properties are set above so * that /data/local.prop cannot interfere with them. */ start_property_service(); return 0; }
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(); /* Read persistent properties after all default values have been loaded. */ 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; }
int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) { struct sockaddr_un addr; int fd, ret; char *secon; fd = socket(PF_UNIX, type, 0); if (fd < 0) { ERROR("Failed to open socket ‘%s‘: %s\n", name, strerror(errno)); return -1; } memset(&addr, 0 , sizeof(addr)); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name); ret = unlink(addr.sun_path); if (ret != 0 && errno != ENOENT) { ERROR("Failed to unlink old socket ‘%s‘: %s\n", name, strerror(errno)); goto out_close; } secon = NULL; if (sehandle) { ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK); if (ret == 0) setfscreatecon(secon); } ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); if (ret) { ERROR("Failed to bind socket ‘%s‘: %s\n", name, strerror(errno)); goto out_unlink; } setfscreatecon(NULL); freecon(secon); chown(addr.sun_path, uid, gid); chmod(addr.sun_path, perm); INFO("Created socket ‘%s‘ with mode ‘%o‘, user ‘%d‘, group ‘%d‘\n", addr.sun_path, perm, uid, gid); return fd; out_unlink: unlink(addr.sun_path); out_close: close(fd); return -1; }
其名为ANDROID_SOCKET_DIR"/PROP_SERVICE_NAME",也即 "/dev/socket/property_service“
然后调用listen监听这个socket,并将创建的socket描述符赋值给全局变量property_set_fd 。
至于queue_property_triggers_action, 请一层层看进去,会看到有函数处理init.rc里面
<propert_name>=<property_value> 这样的键值对 (字符串比较)。至于所谓的trigger,
其实跟上面对action的分析类似,在此就不赘述了。
(3)那么现在我们来看看我们调用property_set或者property_get的流程。
此处以property_set (platform/system/core/libcutils/properties.c)为例。
int property_set(const char *key, const char *value) { return __system_property_set(key, value); }
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; }
我们看到,是要发送PROP_MSG_SETPROP这个消息的,现在得弄清给谁发。
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)) { // 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. pollfds[0].fd = s; pollfds[0].events = 0; r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); if (r == 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(s); return result; }
这个全局变量:"/dev/socket/" PROP_SERVICE_NAME; 即 "/dev/socket/"”property_service“,
当然其实就是"/dev/socket/property_service“。
然后调用了poll函数。此处调用poll的原因,我看了对应地方的代码注释,自己不甚了解,就
不忽悠了。咱们继续忽悠后面的。 调用了send后,会发生什么呢?
我们将回到init.c的main函数中的for循环中。里面的那个poll就是来处理这个的。
nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; 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(); } }
通过比较两个fd,发现将会执行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; } /* Check socket options here */ 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; getpeercon(s, &source_ctx); if(memcmp(msg.name,"ctl.",4) == 0) { // Keep the old close-socket-early behavior when handling // ctl.* properties. 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); } // Note: bionic‘s property client code assumes that the // property server will not close the socket until *AFTER* // the property is written to memory. close(s); } freecon(source_ctx); break; default: close(s); break; } }
会调用property_service.c里面的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(namelen >= PROP_NAME_MAX) return -1; if(valuelen >= PROP_VALUE_MAX) return -1; if(namelen < 1) return -1; pi = (prop_info*) __system_property_find(name); if(pi != 0) { /* ro.* properties may NEVER be modified once set */ 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‘", name, value); return ret; } } /* If name starts with "net." treat as a DNS property. */ if (strncmp("net.", name, strlen("net.")) == 0) { if (strcmp("net.change", name) == 0) { return 0; } /* * The ‘net.change‘ property is a special property used track when any * ‘net.*‘ property name is updated. It is _ONLY_ updated here. Its value * contains the last updated ‘net.*‘ property. */ property_set("net.change", name); } else if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) { /* * Don‘t write properties to disk until after we have read all default properties * to prevent them from being overwritten by default values. */ 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; }
void property_changed(const char *name, const char *value) { if (property_triggers_enabled) queue_property_triggers(name, value); }
《深入理解Android 卷1》读书笔记 (一)—— Android Init之属性服务 (property_service)