Android系統的開機畫面顯示過程分析(8)

     3. 第三個開機畫面的顯示過程
        第三個開機畫面是由應用程序bootanimation來負責顯示的。應用程序bootanimation在啓動腳本init.rc中被配置成了一個服務,以下所示:
  1. service bootanim /system/bin/bootanimation  
  2.     user graphics  
  3.     group graphics  
  4.     disabled  
  5.     oneshot  
       應用程序bootanimation的用戶和用戶組名稱分別被設置爲graphics。注意, 用來啓動應用程序bootanimation的服務是disable的,即init進程在啓動的時候,不會主動將應用程序bootanimation啓動起來。當SurfaceFlinger服務啓動的時候,它會經過修改系統屬性ctl.start的值來通知init進程啓動應用程序bootanimation,以即可以顯示第三個開機畫面,而當System進程將系統中的關鍵服務都啓動起來以後,ActivityManagerService服務就會通知SurfaceFlinger服務來修改系統屬性ctl.stop的值,以即可以通知init進程中止執行應用程序bootanimation,即中止顯示第三個開機畫面。接下來咱們就分別分析第三個開機畫面的顯示過程和中止過程。
 
      從前面 Android系統進程Zygote啓動過程的源代碼分析 一文能夠知道,Zygote進程在啓動的過程當中,會將System進程啓動起來,而從前面 Android應用程序安裝過程源代碼分析一文 又能夠知道,System進程在啓動的過程(Step 3)中,會調用SurfaceFlinger類的靜態成員函數instantiate來啓動SurfaceFlinger服務。Sytem進程在啓動SurfaceFlinger服務的過程當中,首先會建立一個SurfaceFlinger實例,而後再將這個實例註冊到Service Manager中去。在註冊的過程,前面建立的SurfaceFlinger實例會被一個sp指針引用。從前面 Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析一文 能夠知道,當一個對象第一次被智能指針引用的時候,這個對象的成員函數onFirstRef就會被調用。因爲SurfaceFlinger重寫了父類RefBase的成員函數onFirstRef,所以,在註冊SurfaceFlinger服務的過程當中,將會調用SurfaceFlinger類的成員函數onFirstRef。在調用的過程,就會建立一個線程來啓動第三個開機畫面。
       SurfaceFlinger類實如今文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp 中,它的成員函數onFirstRef的實現以下所示:
  1. void SurfaceFlinger::onFirstRef()  
  2. {  
  3.     run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);  
  4.   
  5.     // Wait for the main thread to be done with its initialization  
  6.     mReadyToRunBarrier.wait();  
  7. }  
        SurfaceFlinger類繼承了Thread類,當它的成員函數run被調用的時候,系統就會建立一個新的線程。這個線程在第一次運行以前,會調用SurfaceFlinger類的成員函數readyToRun來通知SurfaceFlinger,它準備就緒了。當這個線程準備就緒以後,它就會循環執行SurfaceFlinger類的成員函數threadLoop,直到這個成員函數的返回值等於false爲止。
 
        注意,SurfaceFlinger類的成員函數onFirstRef是在System進程的主線程中調用的,它須要等待前面建立的線程準備就緒以後,再繼續往前執行,這個經過調用SurfaceFlinger類的成員變量mReadytoRunBarrier所描述的一個Barrier對象的成員函數wait來實現的。每個Barrier對象內問都封裝了一個條件變量(Condition Variable),而條件變量是用來同步線程的。
        接下來,咱們繼續分析SurfaceFlinger類的成員函數readyToRun的實現,以下所示:
  1. status_t SurfaceFlinger::readyToRun()  
  2. {  
  3.     LOGI(   "SurfaceFlinger's main thread ready to run. "  
  4.             "Initializing graphics H/W...");  
  5.       
  6.     ......  
  7.   
  8.     mReadyToRunBarrier.open();  
  9.   
  10.     /* 
  11.      *  We're now ready to accept clients... 
  12.      */  
  13.   
  14.     // start boot animation  
  15.     property_set("ctl.start""bootanim");  
  16.   
  17.     return NO_ERROR;  
  18. }  

       前面建立的線程用做SurfaceFlinger的主線程。這個線程在啓動的時候,會對設備主屏幕以及OpenGL庫進行初始化。初始化完成以後,接着就會調用SurfaceFlinger類的成員變量mReadyToRunBarrier所描述的一個Barrier對象的成員函數open來喚醒System進程的主線程,以便它能夠繼續往前執行。最後,SurfaceFlinger類的成員函數readyToRun的成員函數會調用函數property_set來將系統屬性「ctl.start」的值設置爲「bootanim」,表示要將應用程序bootanimation啓動起來,以即可以顯示第三個開機畫面。
 
       前面在介紹第二個開機畫面的時候提到,當系統屬性發生改變時,init進程就會接收到一個系統屬性變化通知,這個通知最終是由在init進程中的函數handle_property_set_fd來處理的。
       函數handle_property_set_fd實如今文件system/core/init/property_service.c中,以下所示:
  1. void handle_property_set_fd()  
  2. {  
  3.     prop_msg msg;  
  4.     int s;  
  5.     int r;  
  6.     int res;  
  7.     struct ucred cr;  
  8.     struct sockaddr_un addr;  
  9.     socklen_t addr_size = sizeof(addr);  
  10.     socklen_t cr_size = sizeof(cr);  
  11.   
  12.     if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {  
  13.         return;  
  14.     }  
  15.   
  16.     /* Check socket options here */  
  17.     if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {  
  18.         close(s);  
  19.         ERROR("Unable to recieve socket options\n");  
  20.         return;  
  21.     }  
  22.   
  23.     r = recv(s, &msg, sizeof(msg), 0);  
  24.     close(s);  
  25.     if(r != sizeof(prop_msg)) {  
  26.         ERROR("sys_prop: mis-match msg size recieved: %d expected: %d\n",  
  27.               r, sizeof(prop_msg));  
  28.         return;  
  29.     }  
  30.   
  31.     switch(msg.cmd) {  
  32.     case PROP_MSG_SETPROP:  
  33.         msg.name[PROP_NAME_MAX-1] = 0;  
  34.         msg.value[PROP_VALUE_MAX-1] = 0;  
  35.   
  36.         if(memcmp(msg.name,"ctl.",4) == 0) {  
  37.             if (check_control_perms(msg.value, cr.uid, cr.gid)) {  
  38.                 handle_control_message((char*) msg.name + 4, (char*) msg.value);  
  39.             } else {  
  40.                 ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d\n",  
  41.                         msg.name + 4, msg.value, cr.uid, cr.pid);  
  42.             }  
  43.         } else {  
  44.             if (check_perms(msg.name, cr.uid, cr.gid)) {  
  45.                 property_set((char*) msg.name, (char*) msg.value);  
  46.             } else {  
  47.                 ERROR("sys_prop: permission denied uid:%d  name:%s\n",  
  48.                       cr.uid, msg.name);  
  49.             }  
  50.         }  
  51.         break;  
  52.   
  53.     default:  
  54.         break;  
  55.     }  
  56. }  

        init進程是經過一個socket來接收系統屬性變化事件的。每個系統屬性變化事件的內容都是經過一個prop_msg對象來描述的。在prop_msg對象對,成員變量name用來描述發生變化的系統屬性的名稱,而成員變量value用來描述發生變化的系統屬性的值。系統屬性分爲兩種類型,一種是普通類型的系統屬性,另外一種是控制類型的系統屬性(屬性名稱以「ctl.」開頭)。控制類型的系統屬性在發生變化時,會觸發init進程執行一個命令,而普通類型的系統屬性就不具備這個特性。注意,改變系統屬性是須要權限,所以,函數handle_property_set_fd在處理一個系統屬性變化事件以前,首先會檢查修改系統屬性的進程是否具備相應的權限,這是經過調用函數check_control_perms或者check_perms來實現的。
 
        從前面的調用過程能夠知道,當前發生變化的系統屬性的名稱爲「ctl.start」,它的值被設置爲「bootanim」。因爲這是一個控制類型的系統屬性,所以,在經過了權限檢查以後,另一個函數handle_control_message就會被調用,以即可以執行一個名稱爲「bootanim」的命令。
相關文章
相關標籤/搜索