美文网首页
Android系统属性的初始化以及存储

Android系统属性的初始化以及存储

作者: lenozhao | 来源:发表于2018-03-11 16:51 被阅读0次

    基于 Android 6.0 的源码,分析系统属性的初始化以及存储。

    上一篇结尾有提到属性系统的框架,本文就针对系统属性的初始化以及存储分析一波

    property_frame.png

    说到属性系统就不得不提一下 init 进程,ini t是 Linux 系统中用户空间的第一个进程,进程号为1。Kernel 启动后,在用户空间,启动 init 进程,并调用 init 中的 main() 方法执行 init 进程的职责。对于 init 进程的功能分为4部分:

    • 分析和运行所有的 init.rc 文件;
    • 生成设备驱动节点; (通过rc文件创建)
    • 处理子进程的终止(signal 方式);
    • 提供属性服务

    我们就从 init 的 main() 方法开始 (已省略部分与系统属性无关的代码)

    syste/core/init/init.cpp

    int main(int argc, char** argv) {
        ......
            // 创建一块共享的内存空间,用于属性服务
            property_init();
        ......
        // 加载default.prop文件
        property_load_boot_defaults();
        // 启动属性系统服务(通过Socket通讯)
        start_property_service();
        ......
        // 解析 init.rc 文件
        init_parse_config_file("/init.rc");
        ......
        // 根据属性的当前状态运行所有属性触发器。
        queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
        ......
        while (true) {
           ......
            epoll_event ev;
            //循环 等待事件发生
            int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
            if (nr == -1) {
                ERROR("epoll_wait failed: %s\n", strerror(errno));
            } else if (nr == 1) {
                ((void (*)()) ev.data.ptr)();
            }
        }
        return 0;
    }
    

    加载default.prop文件

    syste/core/init/property_service.cpp

    void property_load_boot_defaults() {
        // PROP_PATH_RAMDISK_DEFAULT = "/default.prop"
        load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
    }
    
    
    
    /*
     * Filter is used to decide which properties to load: NULL loads all keys,
     * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
     */
    static void load_properties_from_file(const char* filename, const char* filter) {
        Timer t;
        std::string data;
        // 读取 filename 对应文件内容到 data
        if (read_file(filename, &data)) {
            data.push_back('\n');
            // 解析 data
            load_properties(&data[0], filter);
        }
        NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
    }
    
    
    /*
     * Filter is used to decide which properties to load: NULL loads all keys,
     * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
     */
    static void load_properties(char *data, const char *filter)
    {
        char *key, *value, *eol, *sol, *tmp, *fn;
        size_t flen = 0;
    
        if (filter) {
            flen = strlen(filter);
        }
    
        sol = data;
        // 逐行解析
        while ((eol = strchr(sol, '\n'))) {
            key = sol;
            *eol++ = 0;
            sol = eol;
    
            while (isspace(*key)) key++;
            // 如果是以 '#' 开头则跳过
            if (*key == '#') continue;
    
            tmp = eol - 2;
            while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
            // 如果是以 import 开头则继续解析该文件
            if (!strncmp(key, "import ", 7) && flen == 0) {
                fn = key + 7;
                while (isspace(*fn)) fn++;
                key = strchr(fn, ' ');
                if (key) {
                    *key++ = 0;
                    while (isspace(*key)) key++;
                }
                // 解析被 import 的文件
                load_properties_from_file(fn, key);
    
            } else {
                value = strchr(key, '=');
                if (!value) continue;
                *value++ = 0;
                tmp = value - 2;
                while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
                while (isspace(*value)) value++;
                if (flen > 0) {
                    if (filter[flen - 1] == '*') {
                        if (strncmp(key, filter, flen - 1)) continue;
                    } else {
                        if (strcmp(key, filter)) continue;
                    }
                }
                // 设置系统属性
                property_set(key, value);
            }
        }
    }
    

    系统性的保存

    这里又有 property_set,前面的文章也讲了设置系统属性,不过那是客户端,现在我们看一下服务端又做什么些什么。

    syste/core/init/property_service.cpp

    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;
    }
    
    
    
    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;
        // 如果属性的名称等于“selinux.reload_policy”,并且前面给它设置的值等于1
        if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
            // 重新加载SEAndroid策略
            if (selinux_reload_policy() != 0) {
                ERROR("Failed to reload policy\n");
            }
        } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
            // 恢复SELinux文件属性即恢复文件的安全上下文
            if (restorecon_recursive(value) != 0) {
                ERROR("Failed to restorecon_recursive %s\n", value);
            }
        }
        // 从属性内存区域中查询名称为name的属性
        prop_info* pi = (prop_info*) __system_property_find(name);
        // 如果查询结果不为空,即已经存在
        if(pi != 0) {
            /* ro.* properties may NEVER be modified once set */
            // 如果 name 以  '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.”开头,但是又不等于“net.change”,那么就将名称为“net.change”的属性设置为1,表示网络属性发生了变化
        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);
        } 
        // 如果属性的名称是以“persist.”开头,那么就表示该属性应该是持久化储存到文件中去
        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.
             */
            write_persistent_property(name, value);
        }
        // 发送一个名称为name的属性发生了变化的通知。以便init进程可以执行在启动脚本init.rc中配置的操作。
        property_changed(name, value);
        return 0;
    }
    
    
    
    static void write_persistent_property(const char *name, const char *value)
    {
        char tempPath[PATH_MAX];
        char path[PATH_MAX];
        int fd;
    
        snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
        fd = mkstemp(tempPath);
        if (fd < 0) {
            ERROR("Unable to write persistent property to temp file %s: %s\n", tempPath, strerror(errno));
            return;
        }
        write(fd, value, strlen(value));
        fsync(fd);
        close(fd);
    
        snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
        if (rename(tempPath, path)) {
            unlink(tempPath);
            ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path);
        }
    }
    
    

    函数property_set首先是调用函数__system_property_find检查名称为name的属性是否已经存在属性内存区域中:

    1. 如果存在的话,那么就会得到一个类型为prop_info的结构体pi,表示接下来要做的是修改属性。这时候就会调用我们在上面分析的函数update_prop_info进指定的属性进行修改。
    2. 如果不存在的话,那么指针pi的值就会等于NULL。表示接下来要做的增加属性。这时候只需要在属性内存区域的属性值列表pa_info_array的最后增加一项即可。

    增加或者修改完成属性之后,还要进行以下的检查:

    1. 如果属性的名称是以“net.”开头,但是又不等于“net.change”,那么就将名称为“net.change”的属性设置为1,表示网络属性发生了变化。
    2. 如果属性的名称是以“persist.”开头,那么就表示该属性应该是持久化储存到文件中去,因此就会调用函数write_persistent_property执行这个操作,以便系统下次启动后,可以将该属性的初始值设置为系统上次关闭时的值。
    3. 如果属性的名称等于“selinux.reload_policy”,并且前面给它设置的值等于1,那么就表示要重新加载SEAndroid策略,这是通过调用函数selinux_reload_policy来实现的。

    最后,函数property_set调用另外一个函数property_changed发送一个名称为name的属性发生了变化的通知。以便init进程可以执行在启动脚本init.rc中配置的操作。

    启动属性系统服务

    属性服务就是通过 socket 接收客户端传来的设置系统属性的请求,

    void start_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);
    }
    
    
    
    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);
    
            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;
        }
    }
    
    

    加载其他属性

    开头有讲到,init.cpp 的 main() 方法会去解析 init.rc。在 init.r 中,也会触发加载系统属性,代码如下:

    on late-init
        trigger early-fs
        trigger fs
        trigger post-fs
        trigger post-fs-data
    
        # Load properties from /system/ + /factory after fs mount. Place
        # this in another action so that the load will be scheduled after the prior
        # issued fs triggers have completed.
        trigger load_all_props_action
    
        # Remove a file to wake up anything waiting for firmware.
        trigger firmware_mounts_complete
    
        trigger early-boot
        trigger boot
    
    
    # Load properties from /system/ + /factory after fs mount.
    on load_all_props_action
        load_all_props
        start logd
        start logd-reinit
    

    load_all_props 会调用到 property_service.cpp 中的 load_all_props 方法。

    void load_all_props() {
        load_properties_from_file(PROP_PATH_SYSTEM_BUILD, 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();
    
        load_recovery_id_prop();
    }
    
        #define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
        #define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
        #define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
        #define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"
    

    属性触发器

    参考:
    http://blog.csdn.net/luoshengyang/article/details/38102011
    http://blog.csdn.net/kc58236582/article/details/51939322

    相关文章

      网友评论

          本文标题:Android系统属性的初始化以及存储

          本文链接:https://www.haomeiwen.com/subject/aoswfftx.html