在 init.rc 中,可以见到下面类似的用法,当一个属性值等于XX时,触发下面的事件,比如启动一个进程,或者设置打印级别

on property:sys.init_log_level=*
    loglevel ${sys.init_log_level}
那么它是如何实现的,启动时触发一次?还是任何时刻只要属性值满足条件就触发? 
实验验证结果:
1、启动时,如果属性满足设定条件会触发一次
2、系统运行中如果属性发生变化,且新值等于设定值,则触发一次
代码分析:
init.c (system\core\init)
int main(int argc, char **argv)
{
    ...
    //分配存放属性的共享内存
    property_init();
    ...
    INFO("property init\n");
    property_load_boot_defaults(); //加载默认的属性
    INFO("reading config file\n");
    init_parse_config_file("/init.rc");//解析init.rc文件

    ...
    //处理 on early-init\init section部分,本文暂不关心
    action_for_each_trigger("early-init", action_add_queue_tail);
    action_for_each_trigger("init", action_add_queue_tail);
    //这个是重点
    queue_builtin_action(property_service_init_action, "property_service_init");
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
    //陷入死循环,等待接收别的进程设置属性

    for (;;)
    {
        int nr, i, timeout = -1;
        //重点稍后分析
        execute_one_command();
        restart_processes();
        //使用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(); }
            }
        }
    }
    return 0;
}

.rc 文件中关于类似 on property:sys.init_log_level=* 的解析过程:
int init_parse_config_file(const char *fn)
    parse_config(fn, data);
    return 0;
}
static void parse_config(const char *fn, char *s)
{
    ...
    for (;;) {
        switch (next_token(&state)) {
        ...
        case T_NEWLINE:             //如果是新的一行
            state.line++;
            if (nargs) {
                int kw = lookup_keyword(args[0]);
                if (kw_is(kw, SECTION)) {    //判断是否是section
                    state.parse_line(&state, 0, 0);
                    parse_new_section(&state, kw, nargs, args);
                } else {                     //不是section,那就是command
                    state.parse_line(&state, nargs, args);
                }
                nargs = 0;
            }
            break;
        ...
    }
parser_done:
    ...
}
static void parse_new_section(struct parse_state *state, int kw,
                       int nargs, char **args)
{
    printf("[ %s %s ]\n", args[0],
           nargs > 1 ? args[1] : "");
    switch(kw) {
    case K_service:
        state->context = parse_service(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_service;
            return;
        }
        break;
    case K_on:
        state->context = parse_action(state, nargs, args);
        if (state->context) {
            state->parse_line = parse_line_action;
            return;
        }
        break;
    case K_import:
        parse_import(state, nargs, args);
        break;
    }
    state->parse_line = parse_line_no_op;
}
.rc 文件中有三种 section
1:on 开头的 
on property:sys.init_log_level=*
    loglevel ${sys.init_log_level}
2:service 开头的 
    service netd /system/bin/netd
3:import开头的
import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /init.${ro.zygote}.rc
import /init.trace.rc
这里,我们只关心 on property:sys.init_log_level=* 这种
static void *parse_action(struct parse_state *state, int nargs, char **args)
{
    struct action *act;
    //解析 section 构造一个 action 挂入 action_list
    act = calloc(1, sizeof(*act));
    act->name = args[1];
    list_init(&act->commands);
    list_init(&act->qlist);
    list_add_tail(&action_list, &act->alist);
        /* XXX add to hash */
    return act;
}
static void parse_line_action(struct parse_state* state, int nargs, char **args)
{
    struct command *cmd;
    struct action *act = state->context;
    int (*func)(int nargs, char **args);
    int kw, n;
    //解析这个 section 对应的 commond 挂入 action 的 commands 链表
    cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
    cmd->func = kw_func(kw);
    cmd->line = state->line;
    cmd->filename = state->filename;
    cmd->nargs = nargs;
    memcpy(cmd->args, args, sizeof(char*) * nargs);
    list_add_tail(&act->commands, &cmd->clist);
}
也就是说,在解析完 .rc 文件以后,action_list 链表中填充了大量的 action ,名字类似于property:sys.init_log_level=*,并且可以找到它们对应的 command .

queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
{
    struct action *act;
    struct command *cmd;
    //和解析 .rc 文件一样,构造 action command 放入 action_list
    act = calloc(1, sizeof(*act));
    act->name = name;
    list_init(&act->commands);
    list_init(&act->qlist);
    cmd = calloc(1, sizeof(*cmd));
    cmd->func = func;
    cmd->args[0] = name;
    cmd->nargs = 1;
    list_add_tail(&act->commands, &cmd->clist);
    list_add_tail(&action_list, &act->alist);
    action_add_queue_tail(act);
}
值得注意的是,这里还调用了 action_add_queue_tail(act) 将该 action 挂入了 action_queue 链表,在main函数最后的for循环中,将取出 action_queue 链表中的 action ,调用它们的 command ,后面在分析
这里构造了名字为property_service_init、queue_property_triggers的两个 action ,分别都放入action_list\action_queue链表

来看陷入for循环之后的工作:
for (;;)
    {
        int nr, i, timeout = -1;
        //重点稍后分析
        execute_one_command();
        restart_processes();
               //使用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(); }
            }
        }
    }
1 execute_one_command();
void execute_one_command(void)
{
    if (!cur_action || !cur_command || is_last_command(cur_action, cur_command)) {
        cur_action = action_remove_queue_head(); //从 action_queue 链表中取出 action 
        cur_command = NULL;
    } 
    ret = cur_command->func(cur_command->nargs, cur_command->args);//执行 action 的 command
}

现在来分析一下前面的:
queue_builtin_action(property_service_init_action, "property_service_init");
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");

这里就会调用 property_service_init_action 、queue_property_triggers_action
static int property_service_init_action(int nargs, char **args)
{
    start_property_service(); //创建用于获取属性的 socket,不是关心的重点
    return 0;
}

static int queue_property_triggers_action(int nargs, char **args)
{
    queue_all_property_triggers();
    property_triggers_enabled = 1;
    return 0;
}
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;
                    /* 如果对应的属性存在,判断是否等于设置的值,或者是* */
                    ret = property_get(prop_name, value);
                    if (ret > 0 && (!strcmp(equals + 1, value) ||
                                    !strcmp(equals + 1, "*"))) {
                        //如果判断成功,挂入 action_queue
                        action_add_queue_tail(act);
                    }
                }
            }
        }
    }
}

举例分析:property:sys.init_log_level=*
name sys.init_log_level
value *
如果系统中存在 sys.init_log_level 这个属性,就将这个 action 挂入 action_queue
那么,在下次调用 execute_one_command 的时候,就会调用 sys.init_log_level 对应的 command loglevel ${sys.init_log_level} 设置打印级别
也就验证了实验结论,在系统启动过程中,如果存在满足条件的属性,则触发一次!
下次 execute_one_command 什么时候调用?很简单,poll超时返回时,就回到for的起点,调用了!

poll 有数据时:handle_property_set_fd

handle_property_set_fd
    property_set((char*) msg.name, (char*) msg.value);
        //设置属性
        property_changed(name, value);
void property_changed(const char *name, const char *value)
{
    if (property_triggers_enabled) //前面 queue_property_triggers_action 函数中已经设置为1
        queue_property_triggers(name, value);
}
void queue_property_triggers(const char *name, const char *value)
{
    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:"))) {
            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_list中存在的每一个action的名字做比较(含有property:的action),如果值命中了那么放入 action_queue 链表

在execute_one_command 中就会调用 commmand 了
验证了实验结论中的:系统运行中如果属性发生变化,且新值等于设定值,则触发一次

Android init.rc on property相关推荐

  1. Android init.rc文件解析过程详解(三)

    Android init.rc文件解析过程详解(三) 三.相关结构体 1.listnode listnode结构体用于建立双向链表,这种结构广泛用于kernel代码中, android源代码中定义了l ...

  2. Android init.rc 服务启动不成功

    Android init.rc 服务启动不成功 问题 在开发过程中发现一个问题,我们需要在开机的时候判断硬件版本号去启动服务, 服务的名字是ledservice和ledservice4,但是发现每次烧 ...

  3. 安卓 linux init.rc,[原创]Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  4. Android init.rc文件格式解析

    /****************************************************************************** Android init.rc文件格式解 ...

  5. Android init.rc文件解析过程详解(二)

    Android init.rc文件解析过程详解(二) 3.parse_new_section代码如下: void parse_new_section(struct parse_state *state ...

  6. Android init.rc文件解析过程详解(一)

        Android init.rc文件解析过程详解(一) 一.init.rc文件结构介绍 init.rc文件基本组成单位是section, section分为三种类型,分别由三个关键字(所谓关键字 ...

  7. android init.rc中启动的service 默认是disable的,后续如何启动此服务

    如果 android init.rc中启动的service 默认是disable的,如何才能启动此服务呢? init.rc中可以直接启动service 附带的参数决定启动程序的状态,例如数据业务中配置 ...

  8. Android 系统(242)---Android init.rc执行顺序

    Android init.rc执行顺序 1. 所有的action运行于service之前 2.  下面为各个section的执行顺序,英文编号的section是系统内建的(写死在init.c中的命令) ...

  9. Android init.rc如何启动service去执行sh脚本

    在Android开发中经常会遇到,在应用层想去执行一个脚本来完成某些底层相关的操作,但在应用层又没有root权限. 所以,老大给出一种方法完美解决此问题,又把上层和底层进行了隔离,非常好的策略. 1. ...

  10. android init.rc语法标准 .

    Android 初始化语言由四大类声明组成: 行为类(Actions), 命令类(Commands) ,服务类(Services), 选项类(Options). * 初始化语言以行为单位,由以空格间隔 ...

最新文章

  1. 大数据平台安全标准设计
  2. VMware workstation 14安装windows虚拟机
  3. Spring框架第一天知识总结
  4. 百度关键词抓取工具_VBA利用XMLHTTP抓取百度查询关键词结果的个数
  5. vue使用better-scroll实现下拉刷新、上拉加载
  6. 修改ubuntu的IP地址,静态IP地址
  7. 应用软件更新提醒单页HTML网站源码
  8. python爬取appstore的评论数据的步骤_python数据抓取分析
  9. 浅谈分布式一致性协议之3PC
  10. networkxpdf_1 NetworkX概述
  11. Fragment运行时错误
  12. java isreachable_Java网络编程从入门到精通(12):使用isReachable方法探测主机是否可以连通...
  13. 手机性能测试指标及操作
  14. mysql实验五索引和数据完整性_实验六 索引和数据完整性约束
  15. python基础 // 与 / % 的区别
  16. 单例模式(Python中的单例类)
  17. 汽车温度采集记录测量管理的重要性以及K-TC测量模块介绍 热管理
  18. postgresql 表文件介绍
  19. 开源的NAS软件项目存储
  20. 文献笔记|自我管理相关 self- regulation

热门文章

  1. 机器学习?有无监督、弱监督、半监督、强化、多示例学习是什么
  2. PHP在线网课问答题库搜索,推荐一个大学mooc网课答案题库在线查询公众号
  3. C++ 算术平均数及几何平均数
  4. 计算机桌面计算机快捷方式不见,桌面快捷方式不见了,教您桌面快捷方式不见了怎么办...
  5. 制作风格——百变幻灯片,完全DIY(高级教程)
  6. 电阻转换温度值c语言,PT1000电阻值转化为温度值的计算公式
  7. 工作流引擎的流程业务表设计
  8. 互联网公司干不好上门维修?
  9. css中button宽高大小不包含boder问题和文字不居中问题
  10. 【CVE】CVE-2015-5254:ActiveMQ 反序列化漏洞利用