目录
2 属性服务默认项初始化{property_load_boot_defaults()}
3 启动属性服务{queue_builtin_action(property_service_init_action, "property_service_init")}
4 对action中on property处理{如果属性满足条件}
5 load_all_props_action的执行{on late-init中通过trigger load_all_props_action来处理}
6.1.1 当系统上层{java层或者native层}执行property_set操作时,执行如下操作:
6.1.2 分析主要针对init中的这段代码{接收到属性变更后的处理流程}:
本章关键点总结 & 说明:
说明:思维导图是基于之前文章不断迭代的,本章内容我们关注➕属性服务 部分即可
关于属性服务说明:
android的属性服务类似于windows的注册表服务,系统或者应用会把自己的一些属性储存在注册表中
即使系统重启也能够根据之前注册表中的属性进行相应初始化工作
1 属性服务初始化{property_init()的分析}
property_init的过程关注2件事:创建映射 & 让其他进程知道映射空间
1.1 创建一块共享内存的内存映射空间
创建一块共享内存的内存映射空间{该空间可以通过mmap被其他进程访问},初始化完毕后再次打开
//property_init() -just call->:init_property_area(),实现如下:
static int property_area_inited = 0;
static int init_property_area(void)//初始化属性存储区域
{
if (property_area_inited)//通过全局变量控制,仅执行一次,之后执行无效
return -1;
if(__system_property_area_init())//-just call->:map_prop_area_rw(),打开节点并初始化一块区域并关闭
return -1;
if(init_workspace(&pa_workspace, 0))//重新打开设备节点"/dev/__properties__"
return -1;
fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);//在exec中使用该程序该文件描述符不可用,但在fork中可以
property_area_inited = 1;//表示已经初始化完毕
return 0;
}
其中,关键方法map_prop_area_rw实现如下:
static int map_prop_area_rw()
{
// dev is a tmpfs that we can use to carve a shared workspace out of, so let's do that...
//这里打开的关键节点为"/dev/__properties__"
const int fd = open(property_filename,O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
...
// TODO: Is this really required ? Does android run on any kernels that don't support O_CLOEXEC ?
const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);//在exec中使用该程序该文件描述符不可用,但在fork中可以
...
(ftruncate(fd, PA_SIZE)//ftruncate会将参数fd指定的文件大小改为参数length指定的大小。
pa_size = PA_SIZE; // PA_SIZE被定义为(128 * 1024)
pa_data_size = pa_size - sizeof(prop_area);//这里的prop_area实际上就是数据头而已
compat_mode = false;
void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//mmap
...
prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
__system_property_area__ = pa; //plug into the lib property services
close(fd);
return 0;
}
1.2 其他进程如何知道该共享内存
android采用了gcc的constructor属性,该属性指明一个_libc_preinit函数,当bionic库被加载时将自动调用_libc_preinit,关键call过程如下:
__libc_preinit -call-> __libc_init_common(*args)->__system_properties_init->map_prop_area:
这里更关键分析map_prop_area,实现如下:
static int map_prop_area()
{
int fd(open(property_filename, O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
if (fd >= 0) {
/* For old kernels that don't support O_CLOEXEC */
const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
...
}
bool close_fd = true;
if ((fd < 0) && (errno == ENOENT)) {
fd = get_fd_from_env();
close_fd = false;
}
...
const int map_result = map_fd_ro(fd);
if (close_fd) {
close(fd);
}
return map_result;
}
get_fd_from_env的实现如下:
static int get_fd_from_env(void)
{
char *env = getenv("ANDROID_PROPERTY_WORKSPACE");//一定有某个地方添加了该环境变量,这里取出
if (!env) {
return -1;
}
return atoi(env);
}
map_fd_ro的实现如下:
static int map_fd_ro(const int fd) {
struct stat fd_stat;
(fstat(fd, &fd_stat);
...
pa_size = fd_stat.st_size;
pa_data_size = pa_size - sizeof(prop_area);
void* const map_result = mmap(NULL, pa_size, PROT_READ, MAP_SHARED, fd, 0);
...
prop_area* pa = reinterpret_cast<prop_area*>(map_result);
...
if (pa->version == PROP_AREA_VERSION_COMPAT) {
compat_mode = true;
}
__system_property_area__ = pa;
return 0;
总结:该流程的目的是将init的初始化的那块property属性空间映射到本地进程,以便于使用
2 属性服务默认项初始化{property_load_boot_defaults()}
属性的加载流程如下:
#define PROP_PATH_RAMDISK_DEFAULT "/default.prop"
//property_load_boot_defaults->load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);实现如下
static void load_properties_from_file(const char *fn, const char *filter)
{
char *data;
unsigned sz;
data = read_file(fn, &sz);
if(data != 0) {
load_properties(data, filter);->//这里最终会循环调用property_set(key, value);来设置各个属性
free(data);
}
}
3 启动属性服务{queue_builtin_action(property_service_init_action, "property_service_init")}
这里着重分析property_service_init_action,实现如下:
//property_service_init_action()-just call ->start_property_service();实现如下:
//主要工作为,创建socket,但此时并未接收客户端
void start_property_service(void)
{
int fd;
//creates a Unix domain socket in ANDROID_SOCKET_DIR* ("/dev/socket") as dictated in init.rc
fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL);
if(fd < 0) return;
fcntl(fd, F_SETFD, FD_CLOEXEC);
fcntl(fd, F_SETFL, O_NONBLOCK);
listen(fd, 8);
property_set_fd = fd;
}
4 对action中on property处理{如果属性满足条件}
{queue_builtin_action(queue_property_triggers_action, "queue_property_triggers")}
这里着重分析queue_property_triggers_action,实现如下:
//queue_property_triggers_action->queue_all_property_triggers,实现如下:
void queue_all_property_triggers()
{
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:"))) {
/* parse property name and value syntax is property:<name>=<value> */
const char* name = act->name + strlen("property:");
const char* equals = strchr(name, '=');
if (equals) {
char prop_name[PROP_NAME_MAX + 1];
char value[PROP_VALUE_MAX];
int length = equals - name;
if (length > PROP_NAME_MAX) {
ERROR("property name too long in trigger %s", act->name);
} else {
int ret;
memcpy(prop_name, name, length);
prop_name[length] = 0;
/* does the property exist, and match the trigger value? */
ret = property_get(prop_name, value);
if (ret > 0 && (!strcmp(equals + 1, value) ||
!strcmp(equals + 1, "*"))) {
action_add_queue_tail(act);
}
}
}
}
}
}
此过程中,如果属性条件满足on property:<name>=<value>,则将其放在对应的action执行队列中
5 load_all_props_action的执行{on late-init中通过trigger load_all_props_action来处理}
#define PROP_PATH_SYSTEM_BUILD "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop"
#define PROP_PATH_VENDOR_BUILD "/vendor/build.prop"
#define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"
#define PROP_PATH_FACTORY "/factory/factory.prop"
void load_all_props(void)
{
load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT, NULL);
load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
load_override_properties(); //{本地属性会覆盖之前的默认属性}
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties(); //读取"/data/property"下的persist属性并执行property_set
}
6 属性设置变更处理
6.1 属性设置流程
6.1.1 当系统上层{java层或者native层}执行property_set操作时,执行如下操作:
//property_set-just call->__system_property_set
int __system_property_set(const char *key, const char *value)
{
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;
//结构体msg初始化
prop_msg msg;
memset(&msg, 0, sizeof msg);
msg.cmd = PROP_MSG_SETPROP;
strlcpy(msg.name, key, sizeof msg.name);
strlcpy(msg.value, value, sizeof msg.value);
//发送msg消息
const int err = send_prop_msg(&msg);
if (err < 0) {
return err;
}
return 0;
}
继续分析send_prop_msg,实现如下:
static int send_prop_msg(const prop_msg *msg)
{
const int fd = socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0);//创建socket并初始化
...
const size_t namelen = strlen(property_service_socket);
...
(connect(fd, reinterpret_cast<sockaddr*>(&addr), alen)) < 0) //连接property service
...
(send(fd, msg, sizeof(prop_msg), 0));//发送msg消息
...
if (num_bytes == sizeof(prop_msg)) {//写入成功后还要进行poll操作,保证服务端收到请求并正确处理
pollfd pollfds[1];
pollfds[0].fd = fd;
pollfds[0].events = 0;
//如果server端已经close fd,则客户端pollup挂起,这里会检测到
const int poll_result = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));
if (poll_result == 1 && (pollfds[0].revents & POLLHUP) != 0) {
result = 0;
}
}
close(fd);
return result;
}
整个过程实际上就是向property service 服务发送消息并等待处理结束
6.1.2 分析主要针对init中的这段代码{接收到属性变更后的处理流程}:
for(;;) {
...
if (!property_set_fd_init && get_property_set_fd() > 0) {//只有第一次会进入,ufds初始化工作
ufds[fd_count].fd = get_property_set_fd();
ufds[fd_count].events = POLLIN;
ufds[fd_count].revents = 0;
fd_count++;
property_set_fd_init = 1;
}
...
if (!action_queue_empty() || cur_action)
timeout = 0;
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();//处理属性事件
...
}
}
}
handle_property_set_fd的处理实现如下:
void handle_property_set_fd()
{
...
((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size))//accept操作
(getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) //检测socket options
ufds[0].fd = s;
ufds[0].events = POLLIN;
ufds[0].revents = 0;
nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
...
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
...
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) {//如果属性是以ctl.开头,启动后面的服务
// Keep the old close-socket-early behavior when handling ctl.* properties.
close(s);
if (check_control_mac_perms(msg.value, source_ctx)) {//SELinux校验相关
/**handle_control_message
分支(!strcmp(msg,"start"))->msg_start(arg);//service_find_by_name->service_start,启动服务
分支(!strcmp(msg,"stop")) ->msg_stop(arg);//service_find_by_name->service_stop,关闭服务
分支(!strcmp(msg,"restart"))->msg_restart(arg);//service_find_by_name->service_restart,重启服务
*/
handle_control_message((char*) msg.name + 4, (char*) msg.value);
}
...
} else {
if (check_perms(msg.name, source_ctx)) {//SELinux校验相关
property_set((char*) msg.name, (char*) msg.value);//设置属性
}
...
close(s);
}
freecon(source_ctx);
break;
...
}
}
6.2 属性获取流程
//int property_get-call->system_property_get(key, value)
int __system_property_get(const char *name, char *value)
{
const prop_info *pi = __system_property_find(name);
if (pi != 0) {
return __system_property_read(pi, 0, value);
} else {
value[0] = 0;
return 0;
}
}
继续分析__system_property_find,实现如下:
//__system_property_find-call-> __system_property_find_compat(name)
__LIBC_HIDDEN__ const prop_info *__system_property_find_compat(const char *name)
{
prop_area_compat *pa = (prop_area_compat *)__system_property_area__;//共享内存头地址
unsigned count = pa->count;
unsigned *toc = pa->toc;
unsigned len = strlen(name);
prop_info_compat *pi;
if (len >= PROP_NAME_MAX)
return 0;
if (len < 1)
return 0;
while(count--) {//从共享内存中找到相应属性结构体
unsigned entry = *toc++;
if(TOC_NAME_LEN(entry) != len) continue;
pi = TOC_TO_INFO(pa, entry);
if(memcmp(name, pi->name, len)) continue;
return (const prop_info *)pi;//找到则直接返回
}
return 0;//未找到返回0
}
说明:获取属性的流程不再走socket,而是直接从共享内存中读取