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);
-
-
-
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();
-
-
-
-
-
-
-
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;
-
}
-
-
-
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」的命令。