Android系統(tǒng)屬性的初始化以及存儲

基于 Android 6.0 的源碼,分析系統(tǒng)屬性的初始化以及存儲。

上一篇結(jié)尾有提到屬性系統(tǒng)的框架,本文就針對系統(tǒng)屬性的初始化以及存儲分析一波

property_frame.png

說到屬性系統(tǒng)就不得不提一下 init 進(jìn)程,ini t是 Linux 系統(tǒng)中用戶空間的第一個進(jìn)程,進(jìn)程號為1。Kernel 啟動后,在用戶空間,啟動 init 進(jìn)程,并調(diào)用 init 中的 main() 方法執(zhí)行 init 進(jìn)程的職責(zé)。對于 init 進(jìn)程的功能分為4部分:

  • 分析和運行所有的 init.rc 文件;
  • 生成設(shè)備驅(qū)動節(jié)點; (通過rc文件創(chuàng)建)
  • 處理子進(jìn)程的終止(signal 方式);
  • 提供屬性服務(wù)

我們就從 init 的 main() 方法開始 (已省略部分與系統(tǒng)屬性無關(guān)的代碼)

syste/core/init/init.cpp

int main(int argc, char** argv) {
    ......
        // 創(chuàng)建一塊共享的內(nèi)存空間,用于屬性服務(wù)
        property_init();
    ......
    // 加載default.prop文件
    property_load_boot_defaults();
    // 啟動屬性系統(tǒng)服務(wù)(通過Socket通訊)
    start_property_service();
    ......
    // 解析 init.rc 文件
    init_parse_config_file("/init.rc");
    ......
    // 根據(jù)屬性的當(dāng)前狀態(tài)運行所有屬性觸發(fā)器。
    queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
    ......
    while (true) {
       ......
        epoll_event ev;
        //循環(huán) 等待事件發(fā)生
        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 對應(yīng)文件內(nèi)容到 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 開頭則繼續(xù)解析該文件
        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;
                }
            }
            // 設(shè)置系統(tǒng)屬性
            property_set(key, value);
        }
    }
}

系統(tǒng)性的保存

這里又有 property_set,前面的文章也講了設(shè)置系統(tǒng)屬性,不過那是客戶端,現(xiàn)在我們看一下服務(wù)端又做什么些什么。

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”,并且前面給它設(shè)置的值等于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) {
        // 恢復(fù)SELinux文件屬性即恢復(fù)文件的安全上下文
        if (restorecon_recursive(value) != 0) {
            ERROR("Failed to restorecon_recursive %s\n", value);
        }
    }
    // 從屬性內(nèi)存區(qū)域中查詢名稱為name的屬性
    prop_info* pi = (prop_info*) __system_property_find(name);
    // 如果查詢結(jié)果不為空,即已經(jīng)存在
    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”的屬性設(shè)置為1,表示網(wǎng)絡(luò)屬性發(fā)生了變化
    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.”開頭,那么就表示該屬性應(yīng)該是持久化儲存到文件中去
    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);
    }
    // 發(fā)送一個名稱為name的屬性發(fā)生了變化的通知。以便init進(jìn)程可以執(zhí)行在啟動腳本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);
    }
}

函數(shù)property_set首先是調(diào)用函數(shù)__system_property_find檢查名稱為name的屬性是否已經(jīng)存在屬性內(nèi)存區(qū)域中:

  1. 如果存在的話,那么就會得到一個類型為prop_info的結(jié)構(gòu)體pi,表示接下來要做的是修改屬性。這時候就會調(diào)用我們在上面分析的函數(shù)update_prop_info進(jìn)指定的屬性進(jìn)行修改。
  2. 如果不存在的話,那么指針pi的值就會等于NULL。表示接下來要做的增加屬性。這時候只需要在屬性內(nèi)存區(qū)域的屬性值列表pa_info_array的最后增加一項即可。

增加或者修改完成屬性之后,還要進(jìn)行以下的檢查:

  1. 如果屬性的名稱是以“net.”開頭,但是又不等于“net.change”,那么就將名稱為“net.change”的屬性設(shè)置為1,表示網(wǎng)絡(luò)屬性發(fā)生了變化。
  2. 如果屬性的名稱是以“persist.”開頭,那么就表示該屬性應(yīng)該是持久化儲存到文件中去,因此就會調(diào)用函數(shù)write_persistent_property執(zhí)行這個操作,以便系統(tǒng)下次啟動后,可以將該屬性的初始值設(shè)置為系統(tǒng)上次關(guān)閉時的值。
  3. 如果屬性的名稱等于“selinux.reload_policy”,并且前面給它設(shè)置的值等于1,那么就表示要重新加載SEAndroid策略,這是通過調(diào)用函數(shù)selinux_reload_policy來實現(xiàn)的。

最后,函數(shù)property_set調(diào)用另外一個函數(shù)property_changed發(fā)送一個名稱為name的屬性發(fā)生了變化的通知。以便init進(jìn)程可以執(zhí)行在啟動腳本init.rc中配置的操作。

啟動屬性系統(tǒng)服務(wù)

屬性服務(wù)就是通過 socket 接收客戶端傳來的設(shè)置系統(tǒng)屬性的請求,

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 中,也會觸發(fā)加載系統(tǒng)屬性,代碼如下:

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 會調(diào)用到 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"

屬性觸發(fā)器

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容