??? ?3. 第三個開機畫面的顯示過程
?? ? ? ?第三個開機畫面是由應用程序bootanimation來負責顯示的。應用程序bootanimation在啟動腳本init.rc中被配置成了一個服務,如下所示:
- service?bootanim?/system/bin/bootanimation??
- ????user?graphics??
- ????group?graphics??
- ????disabled??
- ????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的實現如下所示:
- void?SurfaceFlinger::onFirstRef()??
- {??
- ????run("SurfaceFlinger",?PRIORITY_URGENT_DISPLAY);??
- ??
- ????//?Wait?for?the?main?thread?to?be?done?with?its?initialization??
- ????mReadyToRunBarrier.wait();??
- }??
?? ? ? ?SurfaceFlinger類繼承了Thread類,當它的成員函數run被調用的時候,系統就會創建一個新的線程。這個線程在第一次運行之前,會調用SurfaceFlinger類的成員函數readyToRun來通知SurfaceFlinger,它準備就緒了。當這個線程準備就緒之后,它就會循環執行SurfaceFlinger類的成員函數threadLoop,直到這個成員函數的返回值等于false為止。
?? ? ? ?注意,SurfaceFlinger類的成員函數onFirstRef是在System進程的主線程中調用的,它需要等待前面創建的線程準備就緒之后,再繼續往前執行,這個通過調用SurfaceFlinger類的成員變量mReadytoRunBarrier所描述的一個Barrier對象的成員函數wait來實現的。每一個Barrier對象內問都封裝了一個條件變量(Condition Variable),而條件變量是用來同步線程的。
?? ? ? ?接下來,我們繼續分析SurfaceFlinger類的成員函數readyToRun的實現,如下所示:
- status_t?SurfaceFlinger::readyToRun()??
- {??
- ????LOGI(???"SurfaceFlinger's?main?thread?ready?to?run.?"??
- ????????????"Initializing?graphics?H/W...");??
- ??????
- ????......??
- ??
- ????mReadyToRunBarrier.open();??
- ??
- ????/*?
- ?????*??We're?now?ready?to?accept?clients...?
- ?????*/??
- ??
- ????//?start?boot?animation??
- ????property_set("ctl.start",?"bootanim");??
- ??
- ????return?NO_ERROR;??
- }??
?? ? ? 前面創建的線程用作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中,如下所示:
- void?handle_property_set_fd()??
- {??
- ????prop_msg?msg;??
- ????int?s;??
- ????int?r;??
- ????int?res;??
- ????struct?ucred?cr;??
- ????struct?sockaddr_un?addr;??
- ????socklen_t?addr_size?=?sizeof(addr);??
- ????socklen_t?cr_size?=?sizeof(cr);??
- ??
- ????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?recieve?socket?options\n");??
- ????????return;??
- ????}??
- ??
- ????r?=?recv(s,?&msg,?sizeof(msg),?0);??
- ????close(s);??
- ????if(r?!=?sizeof(prop_msg))?{??
- ????????ERROR("sys_prop:?mis-match?msg?size?recieved:?%d?expected:?%d\n",??
- ??????????????r,?sizeof(prop_msg));??
- ????????return;??
- ????}??
- ??
- ????switch(msg.cmd)?{??
- ????case?PROP_MSG_SETPROP:??
- ????????msg.name[PROP_NAME_MAX-1]?=?0;??
- ????????msg.value[PROP_VALUE_MAX-1]?=?0;??
- ??
- ????????if(memcmp(msg.name,"ctl.",4)?==?0)?{??
- ????????????if?(check_control_perms(msg.value,?cr.uid,?cr.gid))?{??
- ????????????????handle_control_message((char*)?msg.name?+?4,?(char*)?msg.value);??
- ????????????}?else?{??
- ????????????????ERROR("sys_prop:?Unable?to?%s?service?ctl?[%s]?uid:?%d?pid:%d\n",??
- ????????????????????????msg.name?+?4,?msg.value,?cr.uid,?cr.pid);??
- ????????????}??
- ????????}?else?{??
- ????????????if?(check_perms(msg.name,?cr.uid,?cr.gid))?{??
- ????????????????property_set((char*)?msg.name,?(char*)?msg.value);??
- ????????????}?else?{??
- ????????????????ERROR("sys_prop:?permission?denied?uid:%d??name:%s\n",??
- ??????????????????????cr.uid,?msg.name);??
- ????????????}??
- ????????}??
- ????????break;??
- ??
- ????default:??
- ????????break;??
- ????}??
- }??
?? ? ? ?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”的命令。
本文轉自 Luoshengyang 51CTO博客,原文鏈接:http://blog.51cto.com/shyluo/967040,如需轉載請自行聯系原作者