setProperty在Android开发中太常用了,很多地方都会用它来记录一下value,以此作为判断条件或者通信的数据。当然ctl.start也可以启动系统服务,前提是要在ServiceManager中有过注册。本篇就来探究下PropertyService的启动过程以及它的处理流程。
按照惯例,还是先列一下会用到的文件:
system/core/init/init.cpp
system/core/init/property_service.cpp
默认属性是在Android系统开机后由init进程收集起来的,init进行到SecondStage的时候会执行下面的过程,从函数命名也能看出来是和property相关的。主要分为三块:service初始化、读入所有的默认属性、启动property service。
int SecondStageMain(int argc, char** argv) {
...
property_init(); //构建所需要的目录,加载属性文件
property_load_boot_defaults(load_debug_prop); //加载系统中的prop文件
StartPropertyService(&epoll); //启动property服务
...
}
前面的初始化部分和加载属性部分暂且略过,代码简洁明了,一看就能明白,主要看一下服务的启动过程,因为这部分会涉及到后面Property是如何set下来的。
void StartPropertyService(Epoll* epoll) {
selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb); //selinux控制属性的set和get
property_set("ro.property_service.version", "2"); //设置属性版本号
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr);//创建一个socket进程同行,用以连接native层的jni
if (property_set_fd == -1) {
PLOG(FATAL) << "start_property_service socket creation failed";
}
listen(property_set_fd, 8); //监听socket文件描述符
if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
//注册到传进来的epoll中
PLOG(FATAL) << result.error();
}
}
这部分看起来也很简洁,通过selinux来控制属性的设置,避免随意的访问,然后创建socket描述符进行监听,并且将其注册到epoll中,这样的话init主函数就可以监听到此处的描述符(后面会介绍),进而当收到消息时去处理,而handle_property_set_fd则是处理函数。
handle_property_set_fd中主要是用来接收socket中传递的消息,借助property_set_fd这个文件描述符来接收和发送消息。当socket有消息来到的时候,通过property_set_fd重新构造一个socket连接,并读取发送过来的消息。接着分开为两个case处理不同的消息,分别为PROP_MSG_SETPROP和PROP_MSG_SETPROP2,从处理逻辑上看出一个传递char数组指针,一个是string指针,殊途同归,只不过一个按照字符、一个按照字符串,最终都是调用HandlePropertySet函数来处理。
static void handle_property_set_fd() {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
if (s == -1) {
return;
}
ucred cr;
socklen_t cr_size = sizeof(cr);
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
close(s);
PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
return;
}
SocketConnection socket(s, cr);
uint32_t timeout_ms = kDefaultSocketTimeout;
uint32_t cmd = 0;
if (!socket.RecvUint32(&cmd, &timeout_ms)) {
PLOG(ERROR) << "sys_prop: error while reading command from the socket";
socket.SendUint32(PROP_ERROR_READ_CMD);
return;
}
switch (cmd) {
case PROP_MSG_SETPROP: {
char prop_name[PROP_NAME_MAX];
char prop_value[PRO