property_set的整体过程--以reboot和bootanim为例
前记
最近看到surfaceflinger在初始化结束后会通过property_set的方式启动开机动画,这种情形很像之前reboot命令中看见的,因此按照之前的分析来分析bootanim,但是发现分析错误,后来深入去了解,才发现我之前分析错了,因此写下这份文档纠正自己的错误,以便以后的学习。
1. Property_set的整个过程
我先纠正一下reboot命令的分析,reboot的源码比较简单,在system/core/reboot/reboot.c中:
ret = property_set(ANDROID_RB_PROPERTY,property_val);
reboot是通过property_set的方式触发propertychange事件,然后才会去真正执行reboot操作。而之前分析错误的原因主要就是property_set的定位出错。
利用SourceInsight进行代码搜索,可以发现有两个地方定义了property_set,分析是在system/core/libcutils/properties.c和system/core/init/property_service.c。当时由于主观臆想定位出错了,现在分析一下如何正确地定位是在哪个源文件实现:
Reboot目录下的Android.mk中是如下定义的:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= reboot.c
LOCAL_SHARED_LIBRARIES:= libcutils
LOCAL_MODULE:= reboot
include $(BUILD_EXECUTABLE)
reboot只包含了libcutils这个动态库,因此property_set就应该是在这里所实现的。而从这个库的名字就可以猜测其是在system/core/libcutils中,我们验证一下;
从system/core/libcutils/Android.mk中可以看到,property_set就是该目录下的properties.c中定义的。
在properties.c中有三个property_set的定义:
#ifdef HAVE_LIBC_SYSTEM_PROPERTIES /*如果有定义libc,也就是bionic*/
int property_set(const char *key, const char*value)
{
return__system_property_set(key, value);
}
#elif defined(HAVE_SYSTEM_PROPERTY_SERVER) /*如果定义了property_server*/
int property_set(const char *key, const char*value)
{
charsendBuf[1+PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX];
charrecvBuf[1];
intresult = -1;
//ALOGV("PROPERTY SET [%s]: [%s]\n", key, value);
pthread_once(&gInitOnce, init);
if(gPropFd < 0)
return -1;
if(strlen(key) >= PROPERTY_KEY_MAX) return -1;
if(strlen(value) >= PROPERTY_VALUE_MAX) return -1;
memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
sendBuf[0] = (char) kSystemPropertySet;
strcpy(sendBuf+1, key);
strcpy(sendBuf+1+PROPERTY_KEY_MAX, value);
pthread_mutex_lock(&gPropertyFdLock);
/*通过socket -- /tmp/android-sysprop */
if(write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)){
pthread_mutex_unlock(&gPropertyFdLock);
return -1;
}
if(read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
pthread_mutex_unlock(&gPropertyFdLock);
return -1;
}
pthread_mutex_unlock(&gPropertyFdLock);
if(recvBuf[0] != 1)
return -1;
return0;
}
#else
int property_set(const char *key, const char*value)
{
charename[PROPERTY_KEY_MAX + 6];
char*p;
intlen;
int r;
if(strlen(value) >= PROPERTY_VALUE_MAX) return -1;
len =strlen(key);
if(len>= PROPERTY_KEY_MAX) return -1;
memcpy(ename, "PROP_", 5);
memcpy(ename+ 5, key, len + 1);
mutex_lock(&env_lock);
/*通过env*/
#ifdef HAVE_MS_C_RUNTIME
{
char temp[256];
snprintf( temp, sizeof(temp), "%s=%s", ename, value);
putenv(temp);
r= 0;
}
#else
r =setenv(ename, value, 1);
#endif
mutex_unlock(&env_lock);
returnr;
}
#endif
Property_set在Libcutils中有三种方式,可以通过配置进行选择,而通过grep可以知道使用的是libc的方式:
./build/core/combo/include/arch/linux-arm/AndroidConfig.h:249:#defineHAVE_LIBC_SYSTEM_PROPERTIES 1
__system_property_set是在bionic/libc/bionic/system_properties.c中定义的:
int __system_property_set(const char *key, constchar *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;
}
return0;
}
static int send_prop_msg(prop_msg *msg)
{
structpollfd pollfds[1];
structsockaddr_un addr;
socklen_t alen;
size_tnamelen;
int s;
int r;
intresult = -1;
s =socket(AF_LOCAL, SOCK_STREAM, 0);
if(s< 0) {
return result;
}
memset(&addr, 0, sizeof(addr));
namelen= strlen(property_service_socket);
/*
PROP_SERVICE_NAME=property_server
staticconst char property_service_socket[] = "/dev/socket/"PROP_SERVICE_NAME;
*/
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 /* ms */));
if(r == 1 && (pollfds[0].revents & POLLHUP) != 0) {
result = 0;
}else {
result = 0;
}
}
close(s);
returnresult;
}
也就是说,propert_set最终会通过socket(IPC)来通知server处理,那必然会有一个server在接收数据进行处理,而这个server在init进程中:
<system/core/init/init.c>
queue_builtin_action(property_service_init_action,"property_service_init");
static intproperty_service_init_action(int nargs, char **args)
{
start_property_service();
return 0;
}
voidstart_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 alldefault values have been loaded. */
load_persistent_properties();
/*创建socket接收数据*/
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;
}
<main>
for(;;) {
int nr, i, timeout = -1;
execute_one_command();
restart_processes();
if (!property_set_fd_init &&get_property_set_fd() > 0) {
ufds[fd_count].fd =get_property_set_fd(); /*监听property_set_fd*/
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
property_set_fd_init = 1;
}
if (!signal_fd_init &&get_signal_fd() > 0) {
ufds[fd_count].fd =get_signal_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
signal_fd_init = 1;
}
if (!keychord_fd_init &&get_keychord_fd() > 0) {
ufds[fd_count].fd =get_keychord_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
keychord_fd_init = 1;
}
if (process_needs_restart) {
timeout = (process_needs_restart -gettime()) * 1000;
if (timeout < 0)
timeout = 0;
}
if (!action_queue_empty() ||cur_action)
timeout = 0;
#ifBOOTCHART
if (bootchart_count > 0) {
if (timeout < 0 || timeout >BOOTCHART_POLLING_MS)
timeout = BOOTCHART_POLLING_MS;
if (bootchart_step() < 0 ||--bootchart_count == 0) {
bootchart_finish();
bootchart_count = 0;
}
}
#endif
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()) /*如果property_set_fd有POLLIN事件*/
handle_property_set_fd();
else if (ufds[i].fd ==get_keychord_fd())
handle_keychord();
else if (ufds[i].fd ==get_signal_fd())
handle_signal();
}
}
}
<property_service.c>
voidhandle_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;
/*接收property_set的数据*/
if ((s = accept(property_set_fd, (structsockaddr *) &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 socketoptions\n");
return;
}
r = TEMP_FAILURE_RETRY(recv(s, &msg,sizeof(msg), 0));
if(r != sizeof(prop_msg)) {
close(s);
return;
}
switch(msg.cmd) {
case PROP_MSG_SETPROP:
msg.name[PROP_NAME_MAX-1] = 0;
msg.value[PROP_VALUE_MAX-1] = 0;
/*校验property是否合法的属性*/
if (!is_legal_property_name(msg.name,strlen(msg.name))) {
ERROR("sys_prop: illegalproperty name. Got: \"%s\"\n", msg.name);
close(s);
return;
}
getpeercon(s, &source_ctx);
/*如果是带有”ctl.”开头的属性,是具有控制操作的,另作处理,这就是bootanim的处理过程*/
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)) {
/*当初定位错误就是在这里,分析reboot的时候直接跳到这一步*/
property_set((char*)msg.name, (char*) msg.value);
} else {
}
close(s);
}
freecon(source_ctx);
break;
default:
close(s);
break;
}
}
1.1 reboot的处理
对于reboot的处理,就是第二分支,调用了property_set来进行处理,而这个property_set与在reboot.c中调用的property_set并不是同一个,这个后者是在libc定义的,前者是在property_service中定义的。
<system/core/init/property_service.c>
int property_set(const char *name, const char*value)
{
prop_info *pi;
intret;
size_tnamelen = strlen(name);
size_tvaluelen = 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) {
/*ro.* properties may NEVER be modified once set */
if(!strncmp(name, "ro.", 3)) return -1;
__system_property_update(pi, value, valuelen); /*更新到property的内存区域中*/
} else{
ret = __system_property_add(name, namelen, value, valuelen); /*新增一个属性*/
if(ret < 0) {
ERROR("Failed to set '%s'='%s'\n", name, value);
return ret;
}
}
/* Ifname starts with "net." treat as a DNS property. */
if(strncmp("net.", name, strlen("net.")) == 0) {
if(strcmp("net.change", name) == 0) {
return 0;
}
property_set("net.change", name);
} elseif (persistent_properties_loaded &&
strncmp("persist.", name, strlen("persist.")) == 0){
write_persistent_property(name, value);
} elseif (strcmp("selinux.reload_policy", name) == 0 &&
strcmp("1", value) == 0) {
selinux_reload_policy();
}
property_changed(name, value);
#ifdefAW_BOOSTUP_ENABLE
aw_boost_up_perf(name,value);
#endif
return0;
}
void property_changed(const char *name, constchar *value)
{
if(property_triggers_enabled)
queue_property_triggers(name, value);
}
void queue_property_triggers(const char *name,const char *value)
{
structlistnode *node;
structaction *act;
/*遍历所有注册的action*/
list_for_each(node, &action_list) {
act = node_to_item(node, struct action, alist);
/*匹配带有property的action,直到与property_set的属性相同的action,然后执行相应操作*/
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); /*添加到action队列中,等待init进程执行*/
}
}
}
}
Reboot的propert_set对应的命令是:
#define ANDROID_RB_PROPERTY"sys.powerctl"
Init.rc中对应的sys.powerctlaction是如下定义的:
on property:sys.powerctl=*
powerctl ${sys.powerctl}
无论powerctl为何值,都会执行{powerctl${sys.powerctl}}这个操作,powerctl对应的函数在keyword.h中有定义—do_powerctl,这里就不进行分析了。
1.2 bootanim的处理
Bootanim对应于第一个分支,我们可以看看surfaceflinger是如何启动bootanim的:
void SurfaceFlinger::startBootAnim() {
//start boot animation
property_set("service.bootanim.exit", "0");
property_set("ctl.start","bootanim"); /*主要是这一个操作*/
}
<init.rc>
service bootanim /system/bin/bootanimation
classmain
usersystem
groupsystem
disabled /*在init.rc中bootanim是disable的,因为需要等待surfaceflinger的初始化完成*/
oneshot
当startBootAnim()后,就可以运行开机动画了。我们看一下property_service是如何实现的:
void handle_control_message(const char *msg,const char *arg)
{
if(!strcmp(msg,"start")) { /*ctl后带的是start*/
msg_start(arg);
} elseif (!strcmp(msg,"stop")) {
msg_stop(arg);
} elseif (!strcmp(msg,"restart")) {
msg_restart(arg);
} else{
ERROR("unknown control msg '%s'\n", msg);
}
}
static void msg_start(const char *name)
{
structservice *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);
}
Property_service在收到ctl族的property后,先去找到这个service,然后启动它。
struct service *service_find_by_name(const char*name)
{
structlistnode *node;
structservice *svc;
/*service_list 就是保存了init.rc中使用service声明的action,bootanim就是一个service*/
list_for_each(node, &service_list) {
svc = node_to_item(node, struct service, slist);
if(!strcmp(svc->name, name)) {
return svc;
}
}
return0;
}
Service_start比较复杂,就不展开说明了,其实主要就是解析init.rc的配置,通过fork创建一个新进程,然后加载其elf文件运行。
Property_set的整个过程就是如此简单,不过当初的武断差点走了歪路,不过现在已经搞清楚整个过程了。