android属性服务
在android中有个注册表机制,那就是属性服务,init在启动的过程会启动这个属性服务,并且在内存中建立一块存储区域,用来存储这些属性,当需要读取这些属性时,就直接从这一内存区域读取,当需要修改这些属性时,就通过属性服务Socket来链接属性服务完成
init.cpp文件的main函数主要通过分别调用property_init()函数和start_property_service()函数来初始化属性和启动属性服务,下面来看看这两个属性
void property_init() {
if (property_area_initialized) {
return;
}
property_area_initialized = true;
if (__system_property_area_init()) {
return;
}
pa_workspace.size = 0;
pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
if (pa_workspace.fd == -1) {
ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
return;
}
}
property_init函数的变量property_area_initialized 代表属性内存区域是否初始化,如果已经初始化,这个值为true,然后直接return回去,如果没有初始化,因为属性服务会使用属性内存区域,所以函数里接着通过open来打开PROP_FILENAME代表的属性内存区域,而PROP_FILENAME具体定义如下:
define PROP_FILENAME “/dev/properties”
下面是start_property_service函数启动属性服务:
void start_property_service() {
//创建socket服务,即属性服务,用于进程间通信,PROP_SERVICE_NAME的定义是"property_service"
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);
if (property_set_fd == -1) {
ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
exit(1);
}
listen(property_set_fd, 8);
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
监听property_set_fd文件描述符,调用register_epoll_handler()函数,当应用程序设置属性时,框架会通过socket链接过来,于是就走到了handle_property_set_fd函数
static void handle_property_set_fd()
{
prop_msg msg;
int s;
int r;
struct ucred cr;
struct sockaddr_un addr;
socklen_t addr_size = sizeof(addr);
socklen_t cr_size = sizeof(cr);
char * source_ctx = NULL;
struct pollfd ufds[1];
const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
int nr;
//接受链接
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;
}
ufds[0].fd = s;
ufds[0].events = POLLIN;
ufds[0].revents = 0;
nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
if (nr == 0) {
ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
close(s);
return;
} else if (nr < 0) {
ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
close(s);
return;
}
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
if(r != sizeof(prop_msg)) {
ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
r, sizeof(prop_msg), strerror(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);
//如果属性以ctl开头,就是控制属性,控制属性用于执行命令,比如开机动画,就是使用这个类型的属性
if(memcmp(msg.name,"ctl.",4) == 0) {
// Keep the old close-socket-early behavior when handling
// ctl.* properties.
close(s);
if (check_control_mac_perms(msg.value, 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, 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;
}
}
handle_property_set_fd函数的变量name用来表示系统属性的名称,而变量value用来表示系统属性的值,系统属性分为2种,一种是普通类型的系统属性,另一种是控制类型的系统属性,而控制类型的系统属性以“ctl”开头。回到函数中,如果属性是普通属性就会调用check_perms函数来检查相应的权限,然后调用property_set函数来设置系统属性,如果是控制类型属性,调用handle_control_message函数来设置系统属性,下面分别来看一下
void handle_control_message(const char *msg, const char *arg)
{
if (!strcmp(msg,"start")) {
msg_start(arg);
} else if (!strcmp(msg,"stop")) {
msg_stop(arg);
} else if (!strcmp(msg,"restart")) {
msg_restart(arg);
} else {
ERROR("unknown control msg '%s'\n", msg);
}
}
handle_control_message函数中,start表示要启动一个服务,stop表示要停止一个服务,restart表示要重启一个服务,
static void msg_start(const char *name)
{
struct service *svc = NULL;
char *tmp = NULL;
char *args = NULL;
if (!strchr(name, ':'))
svc = service_find_by_name(name);
else {
tmp = strdup(name);
if (tmp) {
args = strchr(tmp, ':');
*args = '\0';
args++;
svc = service_find_by_name(tmp);
}
}
if (svc) {
service_start(svc, args);
} else {
ERROR("no such service '%s'\n", name);
}
if (tmp)
free(tmp);
}
msg_start函数的参数name表示服务名称,从前面传过来,而在msg_start函数中,首先调用service_find_by_name函数来找到等于name的服务,这个服务在init.rc中配置,它保存在结构体svc中,接着msg_start函数调用service_start来启动对应的应用程序,Init进程解析init.rc文件的服务之后,也是通过该函数把init.rc的服务启动
设置普通类型的属性:
int property_set(const char* name, const char* value) {
int rc = property_set_impl(name, value);
if (rc == -1) {
ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
}
return rc;
}
property_set函数通过property_set_impl设置属性,如果返回值为-1表示设置属性值失败,接下来看看property_set_impl函数
static int property_set_impl(const char* name, const char* value) {
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;
if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
if (selinux_reload_policy() != 0) {
ERROR("Failed to reload policy\n");
}
} else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
if (restorecon_recursive(value) != 0) {
ERROR("Failed to restorecon_recursive %s\n", value);
}
}
//从属性存储空间看是否已经存在该属性
prop_info* pi = (prop_info*) __system_property_find(name);
if(pi != 0) {
/* ro.* properties may NEVER be modified once set */
//如果属性以ro开头,表示属性只读,不能设置,直接返回
if(!strncmp(name, "ro.", 3)) return -1;
__system_property_update(pi, value, valuelen);
} else {
int rc = __system_property_add(name, namelen, value, valuelen);
if (rc < 0) {
return rc;
}
}
/* If name starts with "net." treat as a DNS property. */
// 以net.开头的属性,主要与DNS有关,
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.
*/
//以persist.开头的属性,通过调用write_persistent_property函数设置value值
write_persistent_property(name, value);
}
property_changed(name, value);
return 0;
}