Android系統(tǒng)啟動——7附錄1:Android屬性系統(tǒng)

本次系列的內(nèi)容如下:

Android啟動流程——1 序言、bootloader引導(dǎo)與Linux啟動
Android系統(tǒng)啟動——2 init進程
Android系統(tǒng)啟動——3 init.rc解析
Android系統(tǒng)啟動——4 zyogte進程
Android系統(tǒng)啟動——5 zyogte進程(Java篇)
Android系統(tǒng)啟動——6 SystemServer啟動
Android系統(tǒng)啟動——7 附錄1:Android屬性系統(tǒng)
Android系統(tǒng)啟動——8 附錄2:相關(guān)守護進程簡介

本篇文章的主要內(nèi)容如下:

  • 1、Android 屬性系統(tǒng)介紹
  • 2、Android的屬性系統(tǒng)與Linux環(huán)境變量
  • 3、Android 屬性系統(tǒng)的創(chuàng)建
  • 4、Android 屬性系統(tǒng)的初始化
  • 5、啟動屬性服務(wù)

一、Android 屬性系統(tǒng)介紹

(一)、介紹

Android 系統(tǒng)的屬性系統(tǒng)(Property)系統(tǒng)有點類似于Window的注冊表,其中的每個屬性被構(gòu)造成鍵值對(key/value)供外界使用。

簡單的來說Android的屬性系統(tǒng)可以簡單的總結(jié)為以下幾點:

  • Android系統(tǒng)一啟動就會從若干屬性腳本文件中加載屬性內(nèi)容
  • Android系統(tǒng)中的所有屬性(key/value)會存入同一塊共享內(nèi)存中
  • 系統(tǒng)中的各個進程會將這塊共享內(nèi)存映射到自己的內(nèi)存空間,這樣就可以直接讀取屬性內(nèi)容了
  • 系統(tǒng)中只有一個實體可以設(shè)置、修改屬性值,它就是屬性系統(tǒng)(init進程)
  • 不同進程只可以通過sockeet方式,向?qū)傩韵到y(tǒng)(init進程)發(fā)出修改,而不能直接修改屬性值
  • 共享內(nèi)存中的鍵值內(nèi)容會以一種字典樹的形式進行組織。

下圖是屬性系統(tǒng)的演示


屬性系統(tǒng).png

(二)、舉例

屬性系統(tǒng)在Android 系統(tǒng)中大量使用,用來保存系統(tǒng)級別的設(shè)置或者在進程間傳遞一些簡單的信息。每個屬性由屬性名稱和屬性值組成,名稱通常是一串‘.’分割的字符串,這些名稱的前綴有特定的含義,不能隨意改動,但是前綴后面的字符串可以由應(yīng)用程序來制定。而且屬性值只能是字符串,如果需要在程序中使用數(shù)值,需要自定完成字符串和數(shù)值之間的轉(zhuǎn)換。

因為Android的 屬性值既可以在Java層調(diào)用,所以Java層獲取和設(shè)置屬性的方法原型如下:

public static String getProperty(String key,  String defaultValue);
public static String setProperty(String key,  String vlaue);

對應(yīng)的Native層獲取和設(shè)置屬性的函數(shù)原型如下:

int property_get(const char *key ,  char *value , const char *default_value)
int property_set(const char *key ,  char *value)

系統(tǒng)中每個進程都可以調(diào)用這些函數(shù)來讀取和修改屬性。讀取屬性值對任何進程都是沒有限制的,直接由本進程從共享區(qū)域讀取;但是修改屬性值必須通過init進程來完成,這樣init進程就可以檢查請求的進程是否有相應(yīng)的權(quán)限來修改該屬性值。如果屬性值修改成功后,init進程會檢查init.rc文件是否已經(jīng)定義了和該屬性值匹配的"觸發(fā)器(trigger)"。如果有定義,則執(zhí)行"觸發(fā)器"下的命令。

舉一個觸發(fā)器的例子

on property:ro.dubuggeable=1
     start console

這個"觸發(fā)器"的含義是:一旦屬性ro.debuggable被設(shè)置為"1",則執(zhí)行命令"start console",啟動console進程。

二、Android的屬性系統(tǒng)與Linux環(huán)境變量

Android的屬性系統(tǒng)表面上看和Linux的環(huán)境變量很類似,都是以字符串的形式保存系統(tǒng)鍵值提供給進程間信息使用。大家很容易弄混他們的區(qū)別,以為他們是一樣的,其實他們在Android系統(tǒng)內(nèi)部是同時存在的。

(一) Android的屬性系統(tǒng)

我們怎么才能查看到Android系統(tǒng)的所有屬性值,其實很簡答

  • 首先 確保,你本地有手機相連接;如果沒有手機,請打開模擬器
  • 其次 找到Android Studio的Terminal,輸入adb shell
  • 最后 進入adb后,輸入getprop

下圖是我的模擬器上的屬性值

Android的屬性系統(tǒng).png

(二) Android的系統(tǒng)環(huán)境變量

那我們怎么才能查看Android系統(tǒng)的環(huán)境變量呢,其實和上面差不多

  • 首先 確保,你本地有手機相連接;如果沒有手機,請打開模擬器
  • 其次 找到Android Studio的Terminal,輸入adb shell
  • 最后 進入adb后,輸入export -p

下圖是我的模擬器上的環(huán)境變量

Android的系統(tǒng)環(huán)境變量.png

屬性系統(tǒng)和環(huán)境變量相比,環(huán)境變量的使用比較隨意,缺乏控制;而屬性系統(tǒng)對名稱的定義以及修改的權(quán)限都增加了限制,增強了安全性,更適合用于程序的配置管理。

三、Android 屬性系統(tǒng)的創(chuàng)建

Android 屬性系統(tǒng) 的啟動是在init進程里面啟動的,前面講解了,init進程是Android 中Linux里面的第一個進程。

我們知道init啟動在init.cppmain()方法里面,按我們就來看下init.cppmain函數(shù),如下:

989 int main(int argc, char** argv) {
        ...
1030        property_init();
        ...
1137    return 0;
1138}

我們看到了在init.cppmain函數(shù)里面調(diào)用了property_init()函數(shù),而property_init()的實現(xiàn)是在property_service.cpp里面,那我們就來看一下

代碼在property_service.cpp 74行

74void property_init() {
75    if (property_area_initialized) {
76        return;
77    }
78
79    property_area_initialized = true;
80
81    if (__system_property_area_init()) {
82        return;
83    }
84
//++++++++++++++++++++++++ 分割線
85    pa_workspace.size = 0;
86    pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
87    if (pa_workspace.fd == -1) {
88        ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
89        return;
90    }
91}

PS: 關(guān)于86行open函數(shù)入?yún)⒌慕忉?/p>

  • O_RDWR:讀寫
  • O_CREAT:若不存在,則創(chuàng)建
  • O_NOFOLLOW:如果filename是軟連接,則打開失敗
  • O_EXCL:如果使用O_CREAT是文件存在,則可返回錯誤信息

我將這個方法的內(nèi)容主要分為兩個部分:

  • 上半部:創(chuàng)建和初始化屬性的共享內(nèi)存空間:
    我們看到里面調(diào)用property_area_initialized來判斷是否初始化過,如果初始化完畢,則直接返回;沒有經(jīng)過初始化則調(diào)用__system_property_area_init()函數(shù)來進行初始化。
  • 下半部:初始化workspace對象:
    然后調(diào)用open函數(shù),來給pa_workspace.fd賦值。

下半部分沒什么好講解的,那我們就來看下上半部分的內(nèi)容

創(chuàng)建和初始化屬性的共享內(nèi)存空間:

__system_property_area_init()函數(shù)這個函數(shù)的具體實現(xiàn)是在bionic中的system_properties.cpp,下面我們就來看下

代碼在system_properties.cpp 796行

596int __system_property_area_init()
597{
598    return map_prop_area_rw();
599}

我們看到好像什么也沒做,就是直接調(diào)用map_prop_area_rw()函數(shù),那我們就來看一下這個map_prop_area_rw()函數(shù)的內(nèi)容

代碼在system_properties.cpp 185行

185static int map_prop_area_rw()
186{
187    /* dev is a tmpfs that we can use to carve a shared workspace
188     * out of, so let's do that...
189     */
//************************* 第1部分 ********************
190    const int fd = open(property_filename,
191                        O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
192
//************************* 第2部分 ********************
193    if (fd < 0) {
194        if (errno == EACCES) {
195            /* for consistency with the case where the process has already
196             * mapped the page in and segfaults when trying to write to it
197             */
198            abort();
199        }
200        return -1;
201    }
202
203    if (ftruncate(fd, PA_SIZE) < 0) {
204        close(fd);
205        return -1;
206    }
207
       //設(shè)置內(nèi)存映射區(qū)表的長度,128kb 
208    pa_size = PA_SIZE;
       //數(shù)據(jù)大小設(shè)置
209    pa_data_size = pa_size - sizeof(prop_area);
210    compat_mode = false;
211
//************************* 第3部分 ********************
       // 將 /dev/__properties__ 設(shè)備文件映射到內(nèi)存中,可讀寫
       // 其中 MAP_SHARED:表示對映射區(qū)域的寫入數(shù)據(jù)會復(fù)制回文件內(nèi),與其它所有映射這個文件的進程共享映射空間
212    void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
213    if (memory_area == MAP_FAILED) {
214        close(fd);
215        return -1;
216    }
217
       //設(shè)置屬性共享區(qū)域的標(biāo)志和版本號,此時共享區(qū)域沒有任何數(shù)據(jù);  
218    prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
219
//************************* 第4部分 ********************
220    /* plug into the lib property services */
221    __system_property_area__ = pa;
222
223    close(fd);
224    return 0;
225}

我將上面的代碼分為4個部分

  • 第1部分:打開屬性區(qū)域用于共享的設(shè)備文件"/dev/_properties"。普通進程只需要讀取屬性值,因此,這里以只讀的方式打開設(shè)備文件。
    O_CLOEXEC:的作用是使進程fork出的子進程自動關(guān)閉這個fd。
    -第2部分:這里做一些準(zhǔn)備工作,比如如果打不開,判斷其原因等
    -第3部分:執(zhí)行mmap來獲得共享區(qū)域的指針,并判斷是否失敗
    -第4部分:最后將屬性共享區(qū)的指針pa保存到全局變量__system_property_area__中。這樣當(dāng)需要讀取某個屬性時,可以直接使用這個全局變量。

其實就是系統(tǒng)把/dev/properties 設(shè)備文件映射到共享內(nèi)存中,并會在該內(nèi)存起始位置設(shè)置共享區(qū)域的標(biāo)志和版本號;最后,會以只讀的方式再打開一次/dev/properties 設(shè)備文件,并將它的fd保存在workspace 結(jié)構(gòu)的對象中。在service_start()函數(shù)中,會將該fd發(fā)布到系統(tǒng)中。

至此,關(guān)于屬性系統(tǒng)共享空間已經(jīng)創(chuàng)建完畢。創(chuàng)建完共享區(qū)域后,接下來就需要初始化系統(tǒng)已有的屬性值,那屬性系統(tǒng)是什么時候初始化的呢?就讓我們來看下

四、Android 屬性系統(tǒng)的初始化

Android屬性系統(tǒng)的初始化的根源也是init.cppmain函數(shù)里面,通過property_load_boot_defaults()函數(shù)來加載默認(rèn)的屬性

989 int main(int argc, char** argv) {
        ...
1077    property_load_boot_defaults();
        ...
1137    return 0;
1138}

property_load_boot_defaults函數(shù)是在property_service.cpp里面,那我們就來看下。

代碼在property_service.cpp 494行

494void property_load_boot_defaults() {
495    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
496}

我們看到property_load_boot_defaults函數(shù)里面什么都沒有做,就是調(diào)用load_properties_from_file函數(shù)。而load_properties_from_file函數(shù)是從PROP_PATH_RAMDISK_DEFAULT表示的文件中讀取屬性值,并設(shè)置到對應(yīng)地方屬性中

在這里涉及幾個常量,為了大家更好的理解,我直接顯示器結(jié)果,如下:

  • PROP_PATH_RAMDISK_DEFAULT:"/default.prop"
  • PROP_PATH_SYSTEM_BUILD:"/system/build.prop"
  • PROP_PATH_VENDOR_BUILD:"/vendor/build.prop"
  • PROP_PATH_LOCAL_OVERRIDE:"/data/local.prop"
  • PROP_PATH_FACTORY:"/factory/factory.prop"

首選我們來看下load_properties_from_file的具體實現(xiàn),也在property_service.cpp

422/*
423 * Filter is used to decide which properties to load: NULL loads all keys,
424 * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
425 */
426static void load_properties_from_file(const char* filename, const char* filter) {
427    Timer t;
428    std::string data;
429    if (read_file(filename, &data)) {
430        data.push_back('\n');
431        load_properties(&data[0], filter);
432    }
433    NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
434}

我們看到在load_properties_from_file函數(shù)中我們看到,它首先讀取文件filename,然后調(diào)用load_properties函數(shù)來裝載屬性值,那我們就研究下load_properties函數(shù)

代碼 property_service.cpp 366行

362/*
363 * Filter is used to decide which properties to load: NULL loads all keys,
364 * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
365 */
366static void load_properties(char *data, const char *filter)
367{
368    char *key, *value, *eol, *sol, *tmp, *fn;
369    size_t flen = 0;
370
371    if (filter) {
372        flen = strlen(filter);
373    }
374
375    sol = data;
376    while ((eol = strchr(sol, '\n'))) {
377        key = sol;
378        *eol++ = 0;
379        sol = eol;
380
381        while (isspace(*key)) key++;
382        if (*key == '#') continue;
383
384        tmp = eol - 2;
385        while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
386
387        if (!strncmp(key, "import ", 7) && flen == 0) {
388            fn = key + 7;
389            while (isspace(*fn)) fn++;
390
391            key = strchr(fn, ' ');
392            if (key) {
393                *key++ = 0;
394                while (isspace(*key)) key++;
395            }
396
397            load_properties_from_file(fn, key);
398
399        } else {
400            value = strchr(key, '=');
401            if (!value) continue;
402            *value++ = 0;
403
404            tmp = value - 2;
405            while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
406
407            while (isspace(*value)) value++;
408
409            if (flen > 0) {
410                if (filter[flen - 1] == '*') {
411                    if (strncmp(key, filter, flen - 1)) continue;
412                } else {
413                    if (strcmp(key, filter)) continue;
414                }
415            }
416
417            property_set(key, value);
418        }
419    }
420}
421

這里的主要內(nèi)容就是把這些屬性發(fā)布到系統(tǒng)中。至此實現(xiàn)了,從文件中導(dǎo)入默認(rèn)的系統(tǒng)屬性。

五、啟動屬性服務(wù)

屬性服務(wù)同樣也是在init中啟動的,代碼也是在init.cppmain函數(shù)里面。

989 int main(int argc, char** argv) {
        ...
1078    start_property_service();
        ...
1137    return 0;
1138}

是通過start_property_service()函數(shù)來啟動屬性服務(wù)的。那我們就來看下start_property_service()函數(shù)的具體實現(xiàn),這個函數(shù)同樣也是在property_service.cpp里面

代碼在property_service.cpp 570行

570void start_property_service() {
571    property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
572                                    0666, 0, 0, NULL);
573    if (property_set_fd == -1) {
574        ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
575        exit(1);
576    }
577
578    listen(property_set_fd, 8);
579
580    register_epoll_handler(property_set_fd, handle_property_set_fd);
581}

我們看到start_property_service函數(shù)創(chuàng)建了socket,然后監(jiān)聽,并且調(diào)用register_epoll_handler函數(shù)把socket的fd放入epoll中。

我們先來先看create_socket的具體實現(xiàn)

1、create_socket函數(shù)解析

代碼在util.cpp

85/*
86 * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR
87 * ("/dev/socket") as dictated in init.rc. This socket is inherited by the
88 * daemon. We communicate the file descriptor's value via the environment
89 * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo").
90 */
91int create_socket(const char *name, int type, mode_t perm, uid_t uid,
92                  gid_t gid, const char *socketcon)
93{
94    struct sockaddr_un addr;
95    int fd, ret;
96    char *filecon;
97
98    if (socketcon)
99        setsockcreatecon(socketcon);
100
101    fd = socket(PF_UNIX, type, 0);
102    if (fd < 0) {
103        ERROR("Failed to open socket '%s': %s\n", name, strerror(errno));
104        return -1;
105    }
106
107    if (socketcon)
108        setsockcreatecon(NULL);
109
110    memset(&addr, 0 , sizeof(addr));
111    addr.sun_family = AF_UNIX;
       // ANDROID_SOCKET_DIR::/dev/socket     這里傳入的name為property_service
       //  這里為socket設(shè)置了設(shè)備文件:/dev/socket/property_service
112    snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s",
113             name);
114
       // 刪掉之前的設(shè)備文件
115    ret = unlink(addr.sun_path);
116    if (ret != 0 && errno != ENOENT) {
117        ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno));
118        goto out_close;
119    }
120
121    filecon = NULL;
122    if (sehandle) {
123        ret = selabel_lookup(sehandle, &filecon, addr.sun_path, S_IFSOCK);
124        if (ret == 0)
125            setfscreatecon(filecon);
126    }
127
       // 將addr 與socket綁定起來
128    ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr));
129    if (ret) {
130        ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno));
131        goto out_unlink;
132    }
133
134    setfscreatecon(NULL);
135    freecon(filecon);
136
        // 設(shè)置權(quán)限
137    chown(addr.sun_path, uid, gid);
138    chmod(addr.sun_path, perm);
139
140    INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n",
141         addr.sun_path, perm, uid, gid);
142
143    return fd;
144
145out_unlink:
146    unlink(addr.sun_path);
147out_close:
148    close(fd);
149    return -1;
150}

我們看到這里是創(chuàng)建了一個socket,并將該socket與"/dev/socket/property_service"這個設(shè)備文件進行綁定。這一點很重要,因為我們在設(shè)置屬性時,會首先拿到該文件的fd,向里面寫數(shù)據(jù);這時epoll會檢測到該socket可讀,從而調(diào)用注冊的事件處理函數(shù)來進行處理;然后在socket進行監(jiān)聽,等待對該socket的連接請求。最后會向epoll_fd注冊這個socket,同時也注冊了事件處理函數(shù)handle_property_set_fd。

下面讓我們來看下register_epoll_handler函數(shù)內(nèi)部的處理邏輯

2、register_epoll_handler函數(shù)解析

代碼在init.cpp 87行

87void register_epoll_handler(int fd, void (*fn)()) {
88    epoll_event ev;
      // 文件描述符可讀
89    ev.events = EPOLLIN;
      // 保存指定的函數(shù)指針,用于后續(xù)的事件處理
90    ev.data.ptr = reinterpret_cast<void*>(fn);
91    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {  
         // 向epoll_fd添加要監(jiān)聽的fd,比如property,keychord和signal事件的監(jiān)聽
92        ERROR("epoll_ctl failed: %s\n", strerror(errno));
93    }
94}

所以當(dāng)eoll輪訓(xùn)發(fā)現(xiàn)此socket有數(shù)據(jù)到來,即有屬性設(shè)置請求時,會調(diào)用handle_property_set_fd()函數(shù)去處理該事件。那我們就來看下事件處理函數(shù)handle_property_set_fd

3、handle_property_set_fd函數(shù)解析

代碼在property_service.cpp 262行

262static void handle_property_set_fd()
263{
264    prop_msg msg;
265    int s;
266    int r;
267    struct ucred cr;
268    struct sockaddr_un addr;
269    socklen_t addr_size = sizeof(addr);
270    socklen_t cr_size = sizeof(cr);
271    char * source_ctx = NULL;
272    struct pollfd ufds[1];
273    const int timeout_ms = 2 * 1000;  /* Default 2 sec timeout for caller to send property. */
274    int nr;
275
       // 等待、處理客戶端的socket連接請求
276    if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
277        return;
278    }
279
280    /* Check socket options here */
        // 通過設(shè)置SO_PEERCRED返回連接到此套接字的進程的憑據(jù),用于檢測對客戶端進程的身份
281    if (getsoc kopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
282        close(s);
283        ERROR("Unable to receive socket options\n");
284        return;
285    }
286
287    ufds[0].fd = s;
288    ufds[0].events = POLLIN;
289    ufds[0].revents = 0;
       // 等待客戶端socket發(fā)送數(shù)據(jù)
290    nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
291    if (nr == 0) {
292        ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
293        close(s);
294        return;
295    } else if (nr < 0) {
296        ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
297        close(s);
298        return;
299    }
300 
       // 獲取客戶端發(fā)送的屬性設(shè)置數(shù)據(jù)
301    r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
       // 判斷數(shù)據(jù)的大小是否合法
302    if(r != sizeof(prop_msg)) {
303        ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
304              r, sizeof(prop_msg), strerror(errno));
305        close(s);
306        return;
307    }
308
       // 識別操作碼
309    switch(msg.cmd) {
310    case PROP_MSG_SETPROP:
311        msg.name[PROP_NAME_MAX-1] = 0;
312        msg.value[PROP_VALUE_MAX-1] = 0;
313
           // 判斷是否合法
314        if (!is_legal_property_name(msg.name, strlen(msg.name))) {
315            ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
316            close(s);
317            return;
318        }
319
320        getpeercon(s, &source_ctx);
321
322        if(memcmp(msg.name,"ctl.",4) == 0) {
323            // Keep the old close-socket-early behavior when handling
324            // ctl.* properties.
325            close(s);
326            if (check_control_mac_perms(msg.value, source_ctx)) {
327                handle_control_message((char*) msg.name + 4, (char*) msg.value);
328            } else {
329                ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
330                        msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
331            }
332        } else {
333            if (check_perms(msg.name, source_ctx)) {
334                property_set((char*) msg.name, (char*) msg.value);
335            } else {
336                ERROR("sys_prop: permission denied uid:%d  name:%s\n",
337                      cr.uid, msg.name);
338            }
339
340            // Note: bionic's property client code assumes that the
341            // property server will not close the socket until *AFTER*
342            // the property is written to memory.
343            close(s);
344        }
345        freecon(source_ctx);
346        break;
347
348    default:
349        close(s);
350        break;
351    }
352}
353

首先,會等待客戶端的連接請求,當(dāng)有客戶端請求連接時,還會去獲取該客戶端進程的ucred結(jié)構(gòu)信息(里面有pid,uid,gid)。最后等待客戶端發(fā)送數(shù)據(jù),并同時準(zhǔn)備接受這些數(shù)據(jù);數(shù)據(jù)收到后,先判斷PROP_MSG_SETPROP操作碼,這是客戶端封裝數(shù)據(jù)時設(shè)置的。接著通過is_legal_property_name函數(shù)檢測請求的屬性名稱是否合法的。檢測完屬性名稱后,如果此時使用的控制類指令"ctl.",則先關(guān)閉此次socket,然后檢測客戶端socket的進程是否有權(quán)限設(shè)置控制類屬性;最后調(diào)用handle_control_message函數(shù)處理該請求。

那我們來看下is_legal_property_name函數(shù)的具體實現(xiàn),

3.1 is_legal_property_name函數(shù)解析

代碼如下:
代碼在property_service.cpp 176行

175static bool is_legal_property_name(const char* name, size_t namelen)
176{
177    size_t i;
178    if (namelen >= PROP_NAME_MAX) return false;
179    if (namelen < 1) return false;
180    if (name[0] == '.') return false;
181    if (name[namelen - 1] == '.') return false;
182
183    /* Only allow alphanumeric, plus '.', '-', or '_' */
184    /* Don't allow ".." to appear in a property name */
185    for (i = 0; i < namelen; i++) {
186        if (name[i] == '.') {
187            // i=0 is guaranteed to never have a dot. See above.
188            if (name[i-1] == '.') return false;
189            continue;
190        }
191        if (name[i] == '_' || name[i] == '-') continue;
192        if (name[i] >= 'a' && name[i] <= 'z') continue;
193        if (name[i] >= 'A' && name[i] <= 'Z') continue;
194        if (name[i] >= '0' && name[i] <= '9') continue;
195        return false;
196    }
197
198    return true;
199}

這里檢測的依據(jù)有:

  • 屬性名稱的長度必須大于等于1,小于32
  • 屬性名稱不能以"."開頭和結(jié)尾
  • 屬性名稱不能出現(xiàn)連續(xù)的"."
  • 屬性的名稱必須以"."為分隔符,且只能使用:'0'-'9'、'a'-'z'、'A'-'Z'、'-'及'_'等字符

3.2 handle_control_message函數(shù)解析

代碼在init.cpp 530行

530void handle_control_message(const char *msg, const char *arg)
531{
532    if (!strcmp(msg,"start")) {
533        msg_start(arg);
534    } else if (!strcmp(msg,"stop")) {
535        msg_stop(arg);
536    } else if (!strcmp(msg,"restart")) {
537        msg_restart(arg);
538    } else {
539        ERROR("unknown control msg '%s'\n", msg);
540    }
541}

就是根據(jù)其傳入的參數(shù),執(zhí)行器相應(yīng)的方法,比如msg_start方法

上一篇文章 Android系統(tǒng)啟動——6 SystemServer啟動
下一篇文章 Android系統(tǒng)啟動——8 附錄2:相關(guān)守護進程簡介

官人[飛吻],你都把臣妾從頭看到尾了,喜歡就點個贊唄(眉眼)!!!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容