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”的命令。