Android SurfaceFlinger 学习之路(三)----Android开机动画流程简述

来源:互联网 发布:小米五双卡网络 编辑:程序博客网 时间:2024/06/03 07:25


http://windrunnerlihuan.com/2017/05/02/Android-SurfaceFlinger-%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF-%E4%B8%89-Android%E5%BC%80%E6%9C%BA%E5%8A%A8%E7%94%BB%E6%B5%81%E7%A8%8B%E7%AE%80%E8%BF%B0/


       在详细分析SurfaceFlinger模块之前要先看看Android的开机动画,因为这个BootAnimation是一个C++应用程序,需要使用SurfaceFlinger服务来创建和渲染自己的Surface,并且不涉及与用户进行交互,所以能以最简洁的方式体现Android与SurfaceFlinger服务的关系。

开机动画的启动

       第一个开机画面是在内核启动的过程中出现的,它是一个静态的画面。第二个开机画面是在init进程启动的过程中出现的,它也是一个静态的画面。第三个开机画面BootAnimation是在系统服务启动的过程中出现的,它是一个动态的画面。无论是哪一个画面,它们都是在一个称为帧缓冲区(frame buffer,简称fb)的硬件设备上进行渲染的。接下来,我们就分别分析第三个画面是如何在fb上显示的。

bootanimation的启动过程

       第三个开机动画使用应用程序BootAnimation来负责显示的,它在启动脚本init.rc中被配置成了一个服务,位于system/core/rootdir/Init.rc:

12345678
......service bootanim /system/bin/bootanimation    class core    user graphics //用户    group graphics audio //用户组    disabled //init进程启动时,不会自动启动bootanimation    oneshot......

       应用程序bootanimation的用户和用户组名称分别被设置为graphics。注意, 用来启动应用程序bootanimation的服务是disable的,即init进程在启动的时候,不会主动将应用程序bootanimation启动起来。
       当SurfaceFlinger服务启动的时候,它会通过修改系统属性ctl.start的值来通知init进程启动应用程序bootanimation,以便可以显示第三个开机画面。

       在早期的Android版本中,SurfaceFlinger服务是由SystemServer启动的。但在Android5.0中,该服务是init进程启动过程中就启动了。在init.rc中能看到对该服务的描述:

1234567
......service surfaceflinger /system/bin/surfaceflinger    class core    user system    group graphics drmrpc    onrestart restart zygote......

       SurfaceFlinger服务的源码路径位于framework/native/services/surfaceflinger/下面,服务的入口在下面的main_surfaceflinger.cpp中,我们查看它的main函数:

1234567891011121314151617181920212223242526272829
int main(int, char**) {    // When SF is launched in its own process, limit the number of    // binder threads to 4.    ProcessState::self()->setThreadPoolMaxThreadCount(4);    // start the thread pool    sp<ProcessState> ps(ProcessState::self());    ps->startThreadPool();    // instantiate surfaceflinger    sp<SurfaceFlinger> flinger = new SurfaceFlinger();#if defined(HAVE_PTHREADS)    setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);#endif    set_sched_policy(0, SP_FOREGROUND);    // initialize before clients can connect    flinger->init(); //我们主要看这里,其他忽略    // publish surface flinger    sp<IServiceManager> sm(defaultServiceManager());    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);    // run in this thread    flinger->run();    return 0;}

       主要工作就是新建一个SurfaceFlinger对象,然后调用其中的init函数,最后调用run函数。我们的重点在init方法,位于framework/native/services/surfaceflinger/SurfaceFlinger.cpp中:

12345
void SurfaceFlinger::init() {...省略大量代码...    // start boot animation    startBootAnim();}

       最后一行调用了startBootAnim函数,我们继续往下看:

12345
void SurfaceFlinger::startBootAnim() {    // start boot animation    property_set("service.bootanim.exit", "0");    property_set("ctl.start", "bootanim");}

       这里讲系统属性”service.bootanim.exit”设置为”0”,并将”ctl.start”设置为”bootanim”。重点是第二个。

       当系统属性发生改变时,init进程就会接收到一个系统属性变化通知,这个通知最终是由在init进程中的函数handle_property_set_fd来处理的。我们可以查看init进程的入口main函数,位于system/core/init/Init.c中:

1234567891011121314151617181920212223242526
int main(int argc, char **argv){...省略大量代码...    for(;;) {        int nr, i, timeout = -1;        ...省略一些代码...        //poll函数用来轮询事件        nr = poll(ufds, fd_count, timeout);        if (nr <= 0)            continue;        for (i = 0; i < fd_count; i++) {            if (ufds[i].revents & POLLIN) {                //我们关注这里,当系统属性值被修改时,得到该事件,会执行handle_property_set_fd函数                if (ufds[i].fd == get_property_set_fd())                    handle_property_set_fd();                else if (ufds[i].fd == get_keychord_fd())                    handle_keychord();                else if (ufds[i].fd == get_signal_fd())                    handle_signal();            }        }    }    return 0;}

       可以看到,init进程会使用poll机制来轮询事件,其中一个事件是系统属性值被修改。得到该事件后,会执行handle_property_set_fd(),位于system/core/init/Property_service.c中:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
void handle_property_set_fd(){    prop_msg msg; //init进程是通过一个socket来接收系统属性变化事件的    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);    char * source_ctx = NULL;    struct pollfd ufds[1];    const int timeout_ms = 2 * 1000;  /* Default 2 sec timeout for caller to send property. */    int nr;    //接收TCP连接 服务端阻塞?等待客户端连接    if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {        return;    }    /* Check socket options here */    //取出socket的可选内容,可能包括客户端进程的权限等属性     if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {        close(s);         ERROR("Unable to receive socket options\n");        return;    }    ufds[0].fd = s;    ufds[0].events = POLLIN;    ufds[0].revents = 0;    //轮询客户端事件    nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));    //轮询事件超时    if (nr == 0) {        ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);        close(s);        return;    //等待错误    } else if (nr < 0) {        ERROR("sys_prop: error waiting for uid=%d to send property message. err=%d %s\n", cr.uid, errno, strerror(errno));        close(s);        return;    }    //接收socket的主体数据     r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));    //接收事件不匹配,return    if(r != sizeof(prop_msg)) {        ERROR("sys_prop: mis-match msg size received: %d expected: %zu errno: %d\n",              r, sizeof(prop_msg), errno);        close(s);        return;    }    //开始条件判断接收事件    switch(msg.cmd) {    case PROP_MSG_SETPROP: //如果是属性发生变化        msg.name[PROP_NAME_MAX-1] = 0;        msg.value[PROP_VALUE_MAX-1] = 0;        //检查属性名,不能有特殊字符,或者两个点..这样的名字        if (!is_legal_property_name(msg.name, strlen(msg.name))) {            ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);            close(s);            return;        }                getpeercon(s, &source_ctx);/*         如果是ctl开头的消息,则认为是控制消息,控制消息用来执行一些命令,例如用adb shell登录后,输入setprop ctl.start bootanim就可以查看开机动画了;输入setprop service.bootanim.exit 1就可以退出开机动画了    */          if(memcmp(msg.name,"ctl.",4) == 0) {            // Keep the old close-socket-early behavior when handling            // ctl.* properties.            close(s);            //改变系统属性是需要权限,所以需要减产是否有权限            if (check_control_mac_perms(msg.value, source_ctx)) {                //通过了权限检查之后,另外一个函数handle_control_message就会被调用,以便可以执行一个名称为“bootanim”的命令                handle_control_message((char*) msg.name + 4, (char*) msg.value);            } else {                ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",                        msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);            }        } else {         /*             不是ctl开头的属性,则首先检查其权限。例如,设置net.开头的属性需要AID_SYSTEM权限,log.开头的属性需要AID_SHELL属性等。         */             if (check_perms(msg.name, source_ctx)) {            // 最后通过property_set函数设置客户端需要设置的属性                  property_set((char*) msg.name, (char*) msg.value);            } else {                ERROR("sys_prop: permission denied uid:%d  name:%s\n",                      cr.uid, msg.name);            }            // Note: bionic's property client code assumes that the            // property server will not close the socket until *AFTER*            // the property is written to memory.            close(s);        }        freecon(source_ctx);        break;    default:        close(s);        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”的命令。

       函数handle_control_message实现在system/core/init/Init.c中,如下所示:

123456789101112
void handle_control_message(const char *msg, const char *arg){    if (!strcmp(msg,"start")) {//所以进入了这里        msg_start(arg);//arg是bootanimation    } else if (!strcmp(msg,"stop")) {        msg_stop(arg);    } else if (!strcmp(msg,"restart")) {        msg_restart(arg);    } else {        ERROR("unknown control msg '%s'\n", msg);    }}

       控制类型的系统属性的名称是以”ctl.”开头,并且是以“start”或者“stop”结尾的,其中,“start”表示要启动某一个服务,而“stop”表示要停止某一个服务,它们是分别通过函数msg_start和msg_stop来实现的。由于当前发生变化的系统属性是以“start”来结尾的,因此,接下来就会调用函数msg_start来启动一个名称为“bootanim”的服务。

       函数msg_start实现在文件system/core/init/Init.c中,如下所示:

12345678910111213141516171819202122232425262728
static void msg_start(const char *name)//bootanim{    struct service *svc = NULL;    char *tmp = NULL;    char *args = NULL;    if (!strchr(name, ':'))//into        //查找init.rc中配置的service列表        svc = service_find_by_name(name);    else {        tmp = strdup(name);        if (tmp) {            args = strchr(tmp, ':');            *args = '\0';            args++;            svc = service_find_by_name(tmp);        }    }    if (svc) {//如果在列表中找到了bootanim,则启动它        service_start(svc, args);    } else {        ERROR("no such service '%s'\n", name);    }    if (tmp)        free(tmp);}

       该函数首先调用service_find_by_name(),从service_list中查询要启动的服务是否有存在,若存在,返回服务的相关信息。因为init.rc中有bootanimation的定义,因此在init进程执行parse_config()时,会将该服务添加到service_list中,所以bootanimation应用是存在的。然后,如果找到了该服务,就调用service_start启动服务。

       到此,bootanimation应用就启动了。

开机动画显示

       从前面的内容可以知道,名称等于“bootanim”的服务所对应的应用程序为/system/bin/bootanimation,这个应用程序实现在frameworks/base/cmds/bootanimation目录中,其中,应用程序入口函数main是实现在frameworks/base/cmds/bootanimation/Bootanimation_main.cpp中的,如下所示:

12345678910111213141516171819202122232425
int main(int argc, char** argv){#if defined(HAVE_PTHREADS)    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);#endif    char value[PROPERTY_VALUE_MAX];    property_get("debug.sf.nobootanimation", value, "0");    int noBootAnimation = atoi(value);    ALOGI_IF(noBootAnimation,  "boot animation disabled");    if (!noBootAnimation) {//检查系统属性“debug.sf.nobootnimaition”的值是否不等于0        //启动一个Binder线程池        sp<ProcessState> proc(ProcessState::self());        ProcessState::self()->startThreadPool();        // create the boot animation object        //创建一个BootAnimation对象        sp<BootAnimation> boot = new BootAnimation();        IPCThreadState::self()->joinThreadPool();    }    return 0;}

       这个函数首先检查系统属性“debug.sf.nobootnimaition”的值是否不等于0。如果不等于的话,那么接下来就会启动一个Binder线程池,并且创建一个BootAnimation对象。这个BootAnimation对象就是用来显示第三个开机画面的。由于BootAnimation对象在显示第三个开机画面的过程中,需要与SurfaceFlinger服务通信,因此,应用程序bootanimation就需要启动一个Binder线程池。

       接着我们看看BootAnimation类的声明,位于frameworks/base/cmds/bootanimation/BootAnimation.h中:

12345678910111213141516171819
class BootAnimation : public Thread, public IBinder::DeathRecipient  {  public:                  BootAnimation();      virtual     ~BootAnimation();        .......  private:      virtual bool        threadLoop();      virtual status_t    readyToRun();      virtual void        onFirstRef();      virtual void        binderDied(const wp<IBinder>& who);         status_t initTexture(Texture* texture, AssetManager& asset, const char* name);      status_t initTexture(const Animation::Frame& frame);      bool android();      bool readFile(const char* name, String8& outString);      bool movie();      ......  };

       BootAnimation类继承了Thread类和IBinder::DeathRecipient类,其中几个重要的函数说明如下:

  • onFirstRef() —– 属于其父类RefBase,该函数在强引用sp新增引用计数時调用,就是当有sp包装的类初始化的时候调用;
  • binderDied() —– 当对象死掉或者其他情况导致该Binder结束时,就会回调binderDied()方法;
  • readyToRun() —– Thread执行前的初始化工作;
  • threadLoop() —– 每个线程类都要实现的,在这里定义thread的执行内容。这个函数如果返回true,且没有requestExist()没有被调用,则该函数会再次执行;如果返回false,则threadloop中的内容仅仅执行一次,线程就会退出。
           其他主要函数的说明如下:
  • android() —– 显示系统默认的开机画面;
  • movie() —– 显示用户自定义的开机动画。

       BootAnimation类成员函数的实现位于frameworks/base/cmds/bootanimation/BootAnimation.cpp。

       BootAnimation类间接地继承了RefBase类,并且重写了RefBase类的成员函数onFirstRef,因此,当一个BootAnimation对象第一次被智能指针引用的时,这个BootAnimation对象的成员函数onFirstRef就会被调用:

1234567
void BootAnimation::onFirstRef() {    status_t err = mSession->linkToComposerDeath(this);    ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));    if (err == NO_ERROR) {        run("BootAnimation", PRIORITY_DISPLAY);    }}

       mSession是BootAnimation类的一个成员变量,它的类型为SurfaceComposerClient,是用来和SurfaceFlinger执行Binder进程间通信的,它是在BootAnimation类的构造函数中创建的,如下所示:

1234
BootAnimation::BootAnimation() : Thread(false), mZip(NULL){    mSession = new SurfaceComposerClient();}

       SurfaceComposerClient类内部有一个实现了ISurfaceComposerClient接口的Binder代理对象mClient,这个Binder代理对象引用了SurfaceFlinger服务,SurfaceComposerClient类就是通过它来和SurfaceFlinger服务通信的。这个我们后面章节会详细分析。

       回到BootAnimation类的成员函数onFirstRef中,由于BootAnimation类引用了SurfaceFlinger服务,因此,当SurfaceFlinger服务意外死亡时,BootAnimation类就需要得到通知,这是通过调用成员变量mSession的成员函数linkToComposerDeath来注册SurfaceFlinger服务的死亡接收通知来实现的。

       BootAnimation类继承了Thread类,因此,当BootAnimation类的成员函数onFirstRef调用了父类Thread的成员函数run之后,系统就会创建一个线程,这个线程在第一次运行之前,会调用BootAnimation类的成员函数readyToRun来执行一些初始化工作,后面再调用BootAnimation类的成员函数htreadLoop来显示第三个开机画面。

       BootAnimation类的成员函数readyToRun的实现如下所示:

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
status_t BootAnimation::readyToRun() {    mAssets.addDefaultAssets();    //检查显示屏信息是否正确    sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(            ISurfaceComposer::eDisplayIdMain));    DisplayInfo dinfo;    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);    if (status)        return -1;    // create the native surface    //调用SurfaceComposerClient对象mSession的成员函数createSurface可以获得一个SurfaceControl对象control    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);    SurfaceComposerClient::openGlobalTransaction();    control->setLayer(0x40000000);    SurfaceComposerClient::closeGlobalTransaction();    //调用SurfaceControl对象control的成员函数getSurface会返回一个Surface对象s    sp<Surface> s = control->getSurface();    // initialize opengl and egl    //初始化OPENEGL和EGL    const EGLint attribs[] = {            EGL_RED_SIZE,   8,            EGL_GREEN_SIZE, 8,            EGL_BLUE_SIZE,  8,            EGL_DEPTH_SIZE, 0,            EGL_NONE    };    EGLint w, h, dummy;    EGLint numConfigs;    EGLConfig config;    EGLSurface surface;    EGLContext context;    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);    eglInitialize(display, 0, 0);    eglChooseConfig(display, attribs, &config, 1, &numConfigs);    surface = eglCreateWindowSurface(display, config, s.get(), NULL);    context = eglCreateContext(display, config, NULL, NULL);    eglQuerySurface(display, surface, EGL_WIDTH, &w);    eglQuerySurface(display, surface, EGL_HEIGHT, &h);    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)        return NO_INIT;    mDisplay = display;    mContext = context;    mSurface = surface;    mWidth = w;    mHeight = h;    mFlingerSurfaceControl = control;    mFlingerSurface = s;    // If the device has encryption turned on or is in process    // of being encrypted we show the encrypted boot animation.    //如果设备加密功能开启了,就要显示加密的开机动画    char decrypt[PROPERTY_VALUE_MAX];    property_get("vold.decrypt", decrypt, "");        bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);    //选取动画文件    ZipFileRO* zipFile = NULL;    if ((encryptedAnimation &&            (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&            ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||            ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&            ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||            ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&            ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {        mZip = zipFile;    }    return NO_ERROR;}

       readyToRun主要做了如下工作:

       1)BootAnimation类的成员函数session用来返回BootAnimation类的成员变量mSession所描述的一个SurfaceComposerClient对象。通过调用SurfaceComposerClient对象mSession的成员函数createSurface可以获得一个SurfaceControl对象control。

       SurfaceComposerClient类的成员函数createSurface首先调用内部的Binder代理对象mClient(frameworks/native/services/surfaceflinger/Client.cpp)来请求SurfaceFlinger返回一个类型为(class Handle : public BBinder, public LayerCleaner)Binder代理对象(封装了SurfaceFlinger的sp指针和Layer对象)handle,和一个IGraphicBufferProducer的sp指针(封装了SurfaceFlinger的sp指针)gbp,接着再使用这两个对象来创建一个SurfaceControl对象。创建出来的SurfaceControl对象的成员变量handle就指向了从SurfaceFlinger返回来的类型为Handle 的Binder代理对象。有了这个Binder代理对象之后,SurfaceControl对象就可以和SurfaceFlinger服务通信了。(下一章节会分析)

       调用SurfaceControl对象control的成员函数getSurface会返回一个Surface对象s。这个Surface对象s内部也有一个类型为IGraphicBufferProducer的sp指针mGraphicBufferProducer,这个sp指针与前面所创建的SurfaceControl对象control的内部的sp指针(封装了SurfaceFlinger的sp指针)gbp引用的是同一个对象。这样,Surface对象s也可以通过其内部的sp指针mGraphicBufferProducer来和SurfaceFlinger服务通信。(下一章节会分析)

       Surface类继承了ANativeWindow类。ANativeWindow类是连接OpenGL和Android窗口系统的桥梁,即OpenGL需要通过ANativeWindow类来间接地操作Android窗口系统。这种桥梁关系是通过EGL库来建立的,所有以egl为前缀的函数名均为EGL库提供的接口。

       2)为了能够在OpenGL和Android窗口系统之间的建立一个桥梁,我们需要一个EGLDisplay对象display,一个EGLConfig对象config,一个EGLSurface对象surface,以及一个EGLContext对象context,其中,EGLDisplay对象display用来描述一个EGL显示屏,EGLConfig对象config用来描述一个EGL帧缓冲区配置参数,EGLSurface对象surface用来描述一个EGL绘图表面,EGLContext对象context用来描述一个EGL绘图上下文(状态),它们是分别通过调用egl库函数eglGetDisplay、eglChooseConfig、eglCreateWindowSurface和eglCreateContext来获得的。注意,EGLConfig对象config、EGLSurface对象surface和EGLContext对象context都是用来描述EGLDisplay对象display的。有了这些对象之后,就可以调用函数eglMakeCurrent来设置当前EGL库所使用的绘图表面以及绘图上下文。

       还有另外一个地方需要注意的是,每一个EGLSurface对象surface有一个关联的ANativeWindow对象。这个ANativeWindow对象是通过函数eglCreateWindowSurface的第三个参数来指定的。在我们这个场景中,这个ANativeWindow对象正好对应于前面所创建的 Surface对象s。每当OpenGL需要绘图的时候,它就会找到前面所设置的绘图表面,即EGLSurface对象surface。有了EGLSurface对象surface之后,就可以找到与它关联的ANativeWindow对象,即Surface对象s。有了Surface对象s之后,就可以通过其内部的sp指针mGraphicBufferProducer来请求SurfaceFlinger服务返回帧缓冲区硬件设备的一个图形访问接口。这样,OpenGL最终就可以将要绘制的图形渲染到帧缓冲区硬件设备中去,即显示在实际屏幕上。屏幕的大小,即宽度和高度,可以通过函数eglQuerySurface来获得。

       3)动画文件的读取是按顺序进行的,如果读取成功,则不再读取后续的文件,如果失败,则读取下一个文件。顺序如下:

  1. 如果设备的加密功能已经开启,或者设备正在进行加密,则读取加密开机动画文件,路径为:

    1
    #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
  2. OEM厂商指定的开机动画,路径为:

    1
    #define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
  3. 系统开机动画,路径为:

    1
    #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"

       这一步执行完成之后,用来显示第三个开机画面的线程的初始化工作就执行完成了,接下来,就会执行这个线程的主体函数,即BootAnimation类的成员函数threadLoop。
       BootAnimation类的成员函数threadLoop的实现如下所示:

1234567891011121314151617181920
bool BootAnimation::threadLoop(){    bool r;    // We have no bootanimation file, so we use the stock android logo    // animation.    if (mZip == NULL) {        r = android();    } else {        r = movie();    }    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);    eglDestroyContext(mDisplay, mContext);    eglDestroySurface(mDisplay, mSurface);    mFlingerSurface.clear();    mFlingerSurfaceControl.clear();    eglTerminate(mDisplay);    IPCThreadState::self()->stopProcess();    return r;}

       这个函数流程比较简单,首先判断自定义的开机动画文件mZip是否存在,如果存在就调用movie()完成自定义开机画面的显示;如果不存在,调用android()完成系统默认开机画面的显示。然后进行开机动画显示后的销毁、释放工作,主要就是readyToRun中初始化的一些EGL对象。最后终止线程,并return。注意,movie()和android()的返回值都是false,因此线程结束也会返回false。threadLoop()函数如果返回值为false,则该函数中的内容只会执行一次;如果返回true,则会不停的执行。这里返回false,因此只会执行一次。

       接下来,我们就分别分析BootAnimation类的成员函数android和movie的实现。

系统默认开机动画android()

       BootAnimation类的成员函数android的实现如下所示:

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
bool BootAnimation::android(){    //读取开机动画默认图片,根据图片创建两个纹理对象    //"android"字样图片    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");    //闪光图片    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");    // clear screen    //清理屏幕    glShadeModel(GL_FLAT);    glDisable(GL_DITHER);    glDisable(GL_SCISSOR_TEST);    glClearColor(0,0,0,1);    glClear(GL_COLOR_BUFFER_BIT);    eglSwapBuffers(mDisplay, mSurface);    glEnable(GL_TEXTURE_2D);    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);    //图片在屏幕中现实位置    const GLint xc = (mWidth  - mAndroid[0].w) / 2;    const GLint yc = (mHeight - mAndroid[0].h) / 2;    const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h);    glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(),            updateRect.height());    // Blend state    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);    const nsecs_t startTime = systemTime();    do {        //计算每次偏移时间,然后计算出偏移位置        nsecs_t now = systemTime();        double time = now - startTime;        float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w;        GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w;        GLint x = xc - offset;        glDisable(GL_SCISSOR_TEST);        glClear(GL_COLOR_BUFFER_BIT);        glEnable(GL_SCISSOR_TEST);        glDisable(GL_BLEND);        //绘制闪光图片,这个会根据上面计算的位置来显示        glBindTexture(GL_TEXTURE_2D, mAndroid[1].name);        glDrawTexiOES(x,                 yc, 0, mAndroid[1].w, mAndroid[1].h);        glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h);        glEnable(GL_BLEND);        //绘制Android字样的图片,这个不动        glBindTexture(GL_TEXTURE_2D, mAndroid[0].name);        glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h);        //交换缓冲区,以显示到屏幕        EGLBoolean res = eglSwapBuffers(mDisplay, mSurface);        if (res == EGL_FALSE)            break;        // 12fps: don't animate too fast to preserve CPU        const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now);        if (sleepTime > 0)            usleep(sleepTime);        //最后执行checkExit函数,判断是否退出了        checkExit();    } while (!exitPending());    glDeleteTextures(1, &mAndroid[0].name);    glDeleteTextures(1, &mAndroid[1].name);    return false;}

       一共有以下几个步骤:
       1) Android系统默认的开机动画是由两张图片android-logo-mask.png和android-logo-shine.png中。这两张图片保存在frameworks/base/core/res/assets/images目录中,它们最终会被编译在framework-res模块(frameworks/base/core/res)中,即编译在framework-res.apk文件中。编译在framework-res模块中的资源文件可以通过AssetManager类来访问。

开机两张图片

       BootAnimation类的成员函数android首先调用另外一个成员函数initTexture来将根据图片android-logo-mask.png和android-logo-shine.png的内容来分别创建两个纹理对象,这两个纹理对象就分别保存在BootAnimation类的成员变量mAndroid所描述的一个数组中。通过混合渲染这两个纹理对象,我们就可以得到一个开机动画,这是通过中间的while循环语句来实现的。

       2)图片android-logo-mask.png用作动画前景,它是一个镂空的“ANDROID”图像。图片android-logo-shine.png用作动画背景,它的中间包含有一个高亮的呈45度角的条纹。在每一次循环中,图片android-logo-shine.png被划分成左右两部分内容来显示。左右两个部分的图像宽度随着时间的推移而此消彼长,这样就可以使得图片android-logo-shine.png中间高亮的条纹好像在移动一样。另一方面,在每一次循环中,图片android-logo-mask.png都作为一个整体来渲染,而且它的位置是恒定不变的。由于它是一个镂空的“ANDROID”图像,因此,我们就可以通过它的镂空来看到它背后的图片android-logo-shine.png的条纹一闪一闪地划过。这个while循环语句会一直被执行,直到应用程序/system/bin/bootanimation被结束为止。

       3)在循环语句最后会执行checkExit()函数:

123456789101112
void BootAnimation::checkExit() {    // Allow surface flinger to gracefully request shutdown    char value[PROPERTY_VALUE_MAX];    property_get(EXIT_PROP_NAME, value, "0");    int exitnow = atoi(value);    if (exitnow) {        requestExit();        if (mAudioPlayer != NULL) {            mAudioPlayer->requestExit();        }    }}

       首先调用property_get获取属性EXIT_PROP_NAME的值:

1
#define EXIT_PROP_NAME "service.bootanim.exit"

       然后判断该值,如果为1,则调用requestExit()要求退出当前线程,该函数是异步的。位于system/core/libutils/Thread.cpp中:

123456
void Thread::requestExit(){    Mutex::Autolock _l(mLock);    //这里将mExitPending 赋值为true    mExitPending = true;}

       回到android()代码:

1
} while (!exitPending());

       调用exitPending(),改函数判断requestExit()是否被调用过,如果调用过则返回true,否则为false。依然位于system/core/libutils/Thread.cpp中:

123456
bool Thread::exitPending() const{    Mutex::Autolock _l(mLock);    //上面我们在requestExit赋过值了    return mExitPending;}

       这样,当属性“service.bootanim.exit”值被设为”1”时,android()就会调用requestExit(),exitPending()返回值为true。于是do…while()循环就会退出,开机动画绘制就会结束。
       至于什么时候是哪个服务将属性“service.bootanim.exit”的值设置为1的,我们后面讲开机动画的停止的时候会提到。

自定义开机动画movie()

       BootAnimation类的成员函数movie的实现比较长,我们分段来阅读:
       Part.1:

12345678910111213141516171819202122
bool BootAnimation::movie(){String8 desString;    //读取desc.txt文件内容    if (!readFile("desc.txt", desString)) {        return false;    }    char const* s = desString.string();    // Create and initialize an AudioPlayer if we have an audio_conf.txt file    //如果存在audio_conf.txt文件,则会创建一个AudioPlayer,并根据读取的字符串初始化    //ignore it    String8 audioConf;    if (readFile("audio_conf.txt", audioConf)) {        mAudioPlayer = new AudioPlayer;        if (!mAudioPlayer->init(audioConf.string())) {            ALOGE("mAudioPlayer.init failed");            mAudioPlayer = NULL;        }    }    ......}

       从前面BootAnimation类的成员函数readyToRun的实现可以知道,如果目标设备上存在压缩文件/system/media/bootanimation.zip(另外两种我们忽略),那么BootAnimation类的成员变量mZip就会指向它,这段代码作用是读取开机动画文件mZip中的描述文件“desc.txt”。每个动画文件压缩包中必须要包含一个desc.txt,该文件用来描述开机动画如何显示。下面以一个示例来分析一下该文件:

12345
480 640 20  p 1 0 folder1  p 2 20 folder2  c 0 0 folder3  c 1 0 folder4

       Part.2:
       第1行用来描述开机动画在屏幕显示的大小及速度。具体为:开机动画的宽度为480个像素,高度为640个像素,显示频率为每秒20帧,即每帧显示1/20秒。

       下面的每一行代表一个片段,显示的时候会按照顺序从上到下依次显示。第1个字符为片段类型,有’c’和’p’两种,两者的区别后面会结合代码说明。

       第2个数字为该片段重复显示的次数,如果为‘0’,表示会无限重复显示;第3个数字为两次显示之间的间隔,单位为第一行中定义的每帧显示的时间;第4个字符串为该片段所在的文件夹,一个片段可以由多个png图片组成,都存放在folder文件夹中。

       “p 1 0 folder1”代表该片段显示1次,与下一个片段间隔0s,该片段的显示图片路径为bootanimation.zip/folder1。
       “p 2 20 folder2”代表该片段显示2次,且两次之间显示的间隔为20(1/20)=1s,与下一个片段间隔20(1/20)=1s,该片段的显示图片路径为bootanimation.zip/folder2。
       “c 0 0 folder3”代表该片段无限循环显示,且两次显示的间隔为0s,与下一个片段间隔0s,该片段的显示图路径为bootanimation.zip/folder3。
       “c 1 10 folder4”代表该片段显示1次,显示后暂停10*(1/20)=0.5s,该片段的显示图路径为bootanimation.zip/folder4。

12345678910111213141516171819202122232425262728293031323334353637383940414243444546
bool BootAnimation::movie(){    ...Part.1...    Animation animation;    // Parse the description file    for (;;) {        const char* endl = strstr(s, "\n");//检测首次出现换行符的地址        if (!endl) break;        //每次读取一行        String8 line(s, endl - s);        const char* l = line.string();        int fps, width, height, count, pause;        char path[ANIM_ENTRY_NAME_MAX];        char color[7] = "000000"; // default to black if unspecified        char pathType;        if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {//每行如果有三个字符串,则依次是宽、高、帧率            // ALOGD("> w=%d, h=%d, fps=%d", width, height, fps);            animation.width = width;            animation.height = height;            animation.fps = fps;        }        //如果是大于等于四个字符串,则依次是显示类型、显示次数、与下一次间隔时间、显示颜色        else if (sscanf(l, " %c %d %d %s #%6s", &pathType, &count, &pause, path, color) >= 4) {            // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color);            Animation::Part part;            part.playUntilComplete = pathType == 'c';            part.count = count;            part.pause = pause;            part.path = path;            part.audioFile = NULL;            if (!parseColor(color, part.backgroundColor)) {                ALOGE("> invalid color '#%s'", color);                part.backgroundColor[0] = 0.0f;                part.backgroundColor[1] = 0.0f;                part.backgroundColor[2] = 0.0f;            }            animation.parts.add(part);        }        s = ++endl;    }    ......}

       上面的for循环语句分析完成desc.txt文件的内容后,就得到了开机动画的显示大小、速度以及片断信息。这些信息都保存在Animation对象animation中,其中,每一个动画片断都使用一个Animation::Part对象来描述,并且保存在Animation对象animation的成员变量parts所描述的一个片断列表中。

       Part.3:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657
bool BootAnimation::movie(){    ...Part.1...    ...Part.2...    // read all the data structures    //开始读取zip文件    const size_t pcount = animation.parts.size();    void *cookie = NULL;    if (!mZip->startIteration(&cookie)) {        return false;    }    ZipEntryRO entry;    char name[ANIM_ENTRY_NAME_MAX];    //开始循环遍历每一个文件    while ((entry = mZip->nextEntry(cookie)) != NULL) {        const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);        if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {            ALOGE("Error fetching entry file name");            continue;        }        const String8 entryName(name);        const String8 path(entryName.getPathDir());        const String8 leaf(entryName.getPathLeaf());        if (leaf.size() > 0) {            for (size_t j=0 ; j<pcount ; j++) {                if (path == animation.parts[j].path) {                    int method;                    // supports only stored png files                    if (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {                        if (method == ZipFileRO::kCompressStored) {                            FileMap* map = mZip->createEntryFileMap(entry);                            if (map) {                                Animation::Part& part(animation.parts.editItemAt(j));                                if (leaf == "audio.wav") {                                    // a part may have at most one audio file                                    part.audioFile = map;                                } else {                                    Animation::Frame frame;                                    frame.name = leaf;                                    frame.map = map;                                    part.frames.add(frame);                                }                            }                        }                    }                }            }        }    }    mZip->endIteration(cookie);    ......}

       接下来,BootAnimation类的成员函数movie再断续将每一个片断所对应的png图片读取出来。每一个png图片都表示一个动画帧,使用一个Animation::Frame对象来描述,并且保存在对应的Animation::Part对象的成员变量frames所描述的一个帧列表中。

       Part.4:

12345678910111213141516171819202122232425262728293031323334
bool BootAnimation::movie(){    ...Part.1...    ...Part.2...    ...Part.3...    // clear screen    glShadeModel(GL_FLAT);    glDisable(GL_DITHER);    glDisable(GL_SCISSOR_TEST);    glDisable(GL_BLEND);    glClearColor(0,0,0,1);    glClear(GL_COLOR_BUFFER_BIT);    eglSwapBuffers(mDisplay, mSurface);    glBindTexture(GL_TEXTURE_2D, 0);    glEnable(GL_TEXTURE_2D);    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    const int xc = (mWidth - animation.width) / 2;    const int yc = ((mHeight - animation.height) / 2);    nsecs_t lastFrame = systemTime();    nsecs_t frameDuration = s2ns(1) / animation.fps;    Region clearReg(Rect(mWidth, mHeight));    clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height));    ......}

       前面的一系列gl函数首先用来清理屏幕,接下来的一系列gl函数用来设置OpenGL的纹理显示方式。
       变量xc和yc的值用来描述开机动画的显示位置,即需要在屏幕中间显示开机动画,另外一个变量frameDuration的值用来描述每一帧的显示时间,它是以纳秒为单位的。
       Region对象clearReg用来描述屏幕中除了开机动画之外的其它区域,它是用整个屏幕区域减去开机动画所点据的区域来得到的。

       Part.5:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
bool BootAnimation::movie(){    ...Part.1...    ...Part.2...    ...Part.3...    ...Part.4...    //第一层for循环用来显示每一个动画片断    for (size_t i=0 ; i<pcount ; i++) {        const Animation::Part& part(animation.parts[i]);        const size_t fcount = part.frames.size();        glBindTexture(GL_TEXTURE_2D, 0);        //第二层的for循环用来循环显示每一个动画片断        for (int r=0 ; !part.count || r<part.count ; r++) {            // Exit any non playuntil complete parts immediately            if(exitPending() && !part.playUntilComplete)                break;            // only play audio file the first time we animate the part            if (r == 0 && mAudioPlayer != NULL && part.audioFile) {                mAudioPlayer->playFile(part.audioFile);            }            glClearColor(                    part.backgroundColor[0],                    part.backgroundColor[1],                    part.backgroundColor[2],                    1.0f);            //第三层的for循环用来显示每一个动画片断所对应的png图片            //可以看到,如果exitPending()返回值为true且part.playUntilComplete=false,则会break。            //即:当SurfaceFlinger服务要求bootanimation停止显示动画时,以‘p’标识的片段会停止,            //而以'c'标识的片段会继续显示。这就是两者之间的主要区别。             for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {                const Animation::Frame& frame(part.frames[j]);                nsecs_t lastFrame = systemTime();                if (r > 0) {                    glBindTexture(GL_TEXTURE_2D, frame.tid);                } else {//如果一个动画片断的循环显示次数不等于1,那么就说明这个动画片断中的png图片需要重复地显示在屏幕中                    //第一次显示一个png图片的时候,会调用函数glGenTextures来为这个png图片创建一个纹理对象,                    //并且将这个纹理对象的名称保存在对应的Animation::Frame对象的成员变量tid中                    if (part.count != 1) {                        glGenTextures(1, &frame.tid);                        glBindTexture(GL_TEXTURE_2D, frame.tid);                        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);                        glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);                    }                    initTexture(frame);                }                // 如果Region对象clearReg所包含的区域不为空,首先要将它所包含的区域裁剪掉,避免开机动画可以显示在指定的位置以及大小中                if (!clearReg.isEmpty()) {                    Region::const_iterator head(clearReg.begin());                    Region::const_iterator tail(clearReg.end());                    glEnable(GL_SCISSOR_TEST);                    while (head != tail) {                        const Rect& r(*head++);                        glScissor(r.left, mHeight - r.bottom,                                r.width(), r.height());                        glClear(GL_COLOR_BUFFER_BIT);                    }                    glDisable(GL_SCISSOR_TEST);                }                glDrawTexiOES(xc, yc, 0, animation.width, animation.height);                eglSwapBuffers(mDisplay, mSurface);                nsecs_t now = systemTime();                nsecs_t delay = frameDuration - (now - lastFrame);                //ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay));                lastFrame = now;                //调用函数usleep函数来让线程睡眠一下,以保证每一个png图片,即每一帧动画都按照预先指定好的速度来显示                if (delay > 0) {                    struct timespec spec;                    spec.tv_sec  = (now + delay) / 1000000000;                    spec.tv_nsec = (now + delay) % 1000000000;                    int err;                    do {                        err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);                    } while (err<0 && errno == EINTR);                }                checkExit();            }            //每当循环显示完成一个片断时,需要调用usleep函数来使得线程睡眠part.pause * ns2us(frameDuration)毫秒,以便可以按照预先设定的节奏来显示开机动画。            usleep(part.pause * ns2us(frameDuration));            // For infinite parts, we've now played them at least once, so perhaps exit            //如果无限循环,则检查是否有退出消息来了,上面android()函数分析过了            if(exitPending() && !part.count)                break;        }        // free the textures for this part        //最后一个if语句判断一个动画片断是否是循环显示的,即循环次数不等于1。        //如果是的话,那么就说明前面为它所对应的每一个png图片都创建过一个纹理对象。        //现在既然这个片断的显示过程已经结束了,因此,就需要释放前面为它所创建的纹理对象。        if (part.count != 1) {            for (size_t j=0 ; j<fcount ; j++) {                const Animation::Frame& frame(part.frames[j]);                glDeleteTextures(1, &frame.tid);            }        }    }    return false;}

       1)第一层for循环用来显示每一个动画片断,第二层的for循环用来循环显示每一个动画片断,第三层的for循环用来显示每一个动画片断所对应的png图片。这些png图片以纹理的方式来显示在屏幕中。

       2)注意,如果一个动画片断的循环显示次数不等于1,那么就说明这个动画片断中的png图片需要重复地显示在屏幕中。由于每一个png图片都需要转换为一个纹理对象之后才能显示在屏幕中,因此,为了避免重复地为同一个png图片创建纹理对象,第三层的for循环在第一次显示一个png图片的时候,会调用函数glGenTextures来为这个png图片创建一个纹理对象,并且将这个纹理对象的名称保存在对应的Animation::Frame对象的成员变量tid中,这样,下次再显示相同的图片时,就可以使用前面已经创建好了的纹理对象,即调用函数glBindTexture来指定当前要操作的纹理对象。

       3)如果Region对象clearReg所包含的区域不为空,那么在调用函数glDrawTexiOES和eglSwapBuffers来显示每一个png图片之前,首先要将它所包含的区域裁剪掉,避免开机动画可以显示在指定的位置以及大小中。

       4) 每当显示完成一个png图片之后,都要将变量frameDuration的值从纳秒转换为毫秒。如果转换后的值大小于,那么就需要调用函数usleep函数来让线程睡眠一下,以保证每一个png图片,即每一帧动画都按照预先指定好的速度来显示。注意,函数usleep指定的睡眠时间只能精确到毫秒,因此,如果预先指定的帧显示时间小于1毫秒,那么BootAnimation类的成员函数movie是无法精确地控制地每一帧的显示时间的。

       5)还有另外一个地方需要注意的是,每当循环显示完成一个片断时,需要调用usleep函数来使得线程睡眠part.pause * ns2us(frameDuration)毫秒,以便可以按照预先设定的节奏来显示开机动画。

       6)最后一个if语句判断一个动画片断是否是循环显示的,即循环次数不等于1。如果是的话,那么就说明前面为它所对应的每一个png图片都创建过一个纹理对象。现在既然这个片断的显示过程已经结束了,因此,就需要释放前面为它所创建的纹理对象。

附注:
       可以看到,如果exitPending()返回值为true且part.playUntilComplete=false,则会break。即:当SurfaceFlinger服务要求bootanimation停止显示动画时,以‘p’标识的片段会停止,而以’c’标识的片段会继续显示。这就是两者之间的主要区别。 我猜想”c”标识的意思是continue,即:即使SurfaceFlinger要求bootanimation停止动画,bootanimation也不会立刻停止动画,它会等c标识片段都显示完毕后,再停止。

       至此,第三个开机画面的显示过程就分析完成了。

开机动画的停止

       接下来,我们再继续分析第三个开机画面是如何停止显示的。

       当System进程将系统中的关键服务启动起来之后,就会将Launcher启动起来。Android应用程序的启动过程实际上就是它的根Activity组件的启动过程。对于应用程序Launcher来说,它的根Activity组件即为Launcher组件。

       一个Activity组件在启动起来之后,就会被记录起来,等到它所运行在的主线程空闲的时候,这个主线程就会向ActivityManagerService发送一个Activity组件空闲的通知。由于应用程序Launcher是系统中第一个被启动的应用程序,即它的根Activity组件是系统中第一个被启动的Activity组件,因此,当ActivityManagerService接收到它的空闲通知的时候,就可以知道系统是刚刚启动起来的。在这种情况下,ActivityManagerService就会停止显示开机动画,以便可以在屏幕中显示应用程序Lancher的界面。

       如果一个线程想要在空闲的时候处理一些事务,那么就必须要向这个线程的消息队列注册一个空闲消息处理器。自定义的空闲消息处理器灯必须要从MessageQueue.IdleHandler类继承下来,并且重写成员函数queueIdle。当一个线程空闲的时候,即消息队列中没有新的消息需要处理的时候,那些注册了的空闲消息处理器的成员函数queueIdle就会被调用。

       应用程序的主线程是通过ActivityThread类来描述的,它实现在文件frameworks/base/core/Java/android/app/ActivityThread.java中。每当有一个新的Activity组件启动起来的时候,ActivityThread类都会向它所描述的应用程序主线程的消息队列注册一个类型为Idler的空闲消息处理器。这样一个应用程序的主线程就可以在空闲的时候,向ActivityManagerService发送一个Activity组件空闲通知,相当于是通知ActivityManagerService,一个新的Activity组件已经准备就绪了。

       Idler类定义在frameworks/base/core/java/android/app/ActivityThread.java中, 它的成员函数queueIdle的实现如下所示:

12345678910111213141516171819202122232425262728
private class Idler implements MessageQueue.IdleHandler {    @Override    public final boolean queueIdle() {        ActivityClientRecord a = mNewActivities;        ......        if (a != null) {            mNewActivities = null;            IActivityManager am = ActivityManagerNative.getDefault();            ActivityClientRecord prev;            do {                .....                if (a.activity != null && !a.activity.mFinished) {                    try {                        am.activityIdle(a.token, a.createdConfig, stopProfiling);                        a.createdConfig = null;                    } catch (RemoteException ex) {                        // Ignore                    }                }                prev = a;                a = a.nextIdle;                prev.nextIdle = null;            } while (a != null);        }        ......        return false;    }}

       ActivityThread类有一个类型为ActivityClientRecord的成员变量mNewActivities,用来描述所有在当前应用程序主线程中新启动起来的Activity组件。这些新启动起来的Activity组件通过ActivityClientRecord类的成员变量nextIdle连接在一起。一旦当前应用程序主线程向ActivityManagerService发送了这些新启动的Activity组件的空闲通知之后,这些新启动起来的Activity组件就不会再被保存在ActivityThread类的成员变量mNewActivities中了,即每一个新启动的Activity组件只有一次机会向ActivityManagerService发送一个空闲通知。

       向ActivityManagerService发送一个Activity组件空闲通知是通过调用ActivityManagerService代理对象的成员函数activityIdle来实现的,而ActivityManagerService代理对象可以通过调用ActivityManagerNative类的静态成员函数getDefault来获得。

       ActivityManagerService代理对象的类型为ActivityManagerProxy,它的成员函数activityIdle实现在文件frameworks/base/core/java/android/app/ActivityManagerNative.java中,如下所示:

123456789101112131415161718192021222324
class ActivityManagerProxy implements IActivityManager  {      ......        public void activityIdle(IBinder token, Configuration config) throws RemoteException      {          Parcel data = Parcel.obtain();          Parcel reply = Parcel.obtain();          data.writeInterfaceToken(IActivityManager.descriptor);          data.writeStrongBinder(token);          if (config != null) {              data.writeInt(1);              config.writeToParcel(data, 0);          } else {              data.writeInt(0);          }          mRemote.transact(ACTIVITY_IDLE_TRANSACTION, data, reply, IBinder.FLAG_ONEWAY);          reply.readException();          data.recycle();          reply.recycle();      }        ......  }

       ActivityManagerProxy类的成员函数activityIdle实际上是向ActivityManagerService发送一个类型为ACTIVITY_IDLE_TRANSACTION的Binder进程间通信请求,其中,参数token用来描述与这个进程间通信请求所关联的一个Activity组件,在我们这个场景中,这个Activity组件即为应用程序Launcher的根Activity组件Launcher。

       类型为ACTIVITY_IDLE_TRANSACTION的Binder进程间通信请求是由ActivityManagerService类的成员函数activityIdle来处理的,如下所示:

1234567891011121314151617181920212223242526272829
public final class ActivityManagerService extends ActivityManagerNative          implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {      ......        @Override    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {        final long origId = Binder.clearCallingIdentity();        synchronized (this) {            ActivityStack stack = ActivityRecord.getStackLocked(token);            if (stack != null) {                ActivityRecord r =                        mStackSupervisor.activityIdleInternalLocked(token, false, config);                if (stopProfiling) {                    if ((mProfileProc == r.app) && (mProfileFd != null)) {                        try {                            mProfileFd.close();                        } catch (IOException e) {                        }                        clearProfilerLocked();                    }                }            }        }        Binder.restoreCallingIdentity(origId);    }       ......  }

       ActivityManagerService有一个类型为ActivityStackSupervisor的成员变量mStackSupervisor,Run all ActivityStacks through this,运行所有的ActivityStacks 通过这个,它的成员函数activityIdleInternalLocked如下:

123456789101112131415161718192021222324252627282930
// Checked.final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,        Configuration config) {    ......    boolean booting = false;    boolean enableScreen = false;    boolean activityRemoved = false;    ActivityRecord r = ActivityRecord.forToken(token);    if (r != null) {        ......        if (isFrontStack(r.task.stack) || fromTimeout) {            booting = mService.mBooting;            mService.mBooting = false;            if (!mService.mBooted) {                mService.mBooted = true;                enableScreen = true;            }        }    }    ......    if (booting || enableScreen) {        mService.postFinishBooting(booting, enableScreen);    }    ......    return r;}

       所以进入if判断中,将booting置为true,mService.mBooted和enableScreen也为true,进入下面的if判断,ActivityManagerService调用postFinishBooting方法完成系统启动。我们进入往下看:

123456789101112131415161718192021222324
void postFinishBooting(boolean finishBooting, boolean enableScreen) {    mHandler.sendMessage(mHandler.obtainMessage(FINISH_BOOTING_MSG,            finishBooting? 1 : 0, enableScreen ? 1 : 0));}final MainHandler mHandler;final class MainHandler extends Handler {    @Override    public void handleMessage(Message msg) {        ......        case FINISH_BOOTING_MSG: {            if (msg.arg1 != 0) {                finishBooting();            }            if (msg.arg2 != 0) {                enableScreenAfterBoot();            }            break;        }        ......    }}

       这里最后调用enableScreenAfterBoot方法,以便可以将屏幕让出来显示应用程序Launcher的界面:

123456789
void enableScreenAfterBoot() {    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,            SystemClock.uptimeMillis());    mWindowManager.enableScreenAfterBoot();    synchronized (this) {        updateEventDispatchingLocked();    }}

       ActivityManagerService类的成员变量mWindowManager指向了系统中的Window管理服务WindowManagerService,ActivityManagerService服务通过调用它的成员函数enableScreenAfterBoot来停止显示开机动画。

       WindowManagerService类的成员函数enableScreenAfterBoot的实现如下所示:

123456789101112131415161718192021222324
public void enableScreenAfterBoot() {    synchronized(mWindowMap) {        if (DEBUG_BOOT) {            RuntimeException here = new RuntimeException("here");            here.fillInStackTrace();            Slog.i(TAG, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled                    + " mForceDisplayEnabled=" + mForceDisplayEnabled                    + " mShowingBootMessages=" + mShowingBootMessages                    + " mSystemBooted=" + mSystemBooted, here);        }        if (mSystemBooted) {            return;        }        mSystemBooted = true;        hideBootMessagesLocked();        // If the screen still doesn't come up after 30 seconds, give        // up and turn it on.        mH.sendEmptyMessageDelayed(H.BOOT_TIMEOUT, 30*1000);    }    mPolicy.systemBooted();    performEnableScreen();}

       WindowManagerService类的成员变量mSystemBooted用来记录系统是否已经启动完成的。如果已经启动完成的话,那么这个成员变量的值就会等于true,这时候WindowManagerService类的成员函数enableScreenAfterBoot什么也不做就返回了,否则的话,WindowManagerService类的成员函数enableScreenAfterBoot首先将这个成员变量的值设置为true,接着再调用另外一个成员函数performEnableScreen来执行停止显示开机动画的操作。
       WindowManagerService类的成员函数performEnableScreen的实现如下所示:

1234567891011121314151617181920212223242526272829303132333435
public void performEnableScreen() {    synchronized(mWindowMap) {        ......        if (mDisplayEnabled) {            return;        }        if (!mSystemBooted && !mShowingBootMessages) {            return;        }        // Don't enable the screen until all existing windows have been drawn.        if (!mForceDisplayEnabled && checkWaitingForWindowsLocked()) {            return;        }        if (!mBootAnimationStopped) {            // Do this one time.            try {                IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");                if (surfaceFlinger != null) {                    //Slog.i(TAG, "******* TELLING SURFACE FLINGER WE ARE BOOTED!");                    Parcel data = Parcel.obtain();                    data.writeInterfaceToken("android.ui.ISurfaceComposer");                    surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED                            data, null, 0);                    data.recycle();                }            } catch (RemoteException ex) {                Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");            }            mBootAnimationStopped = true;        }        ......}

       WindowManagerService类的另外一个成员变量mDisplayEnabled用来描述WindowManagerService是否已经初始化过系统的屏幕了,只有当它的值等于false,并且系统已经完成启动,即WindowManagerService类的成员变量mSystemBooted等于true的情况下,WindowManagerService类的成员函数performEnableScreen才通知SurfaceFlinger服务停止显示开机动画。

       注意,WindowManagerService类的成员函数performEnableScreen是通过一个类型为IBinder.FIRST_CALL_TRANSACTION的进程间通信请求来通知SurfaceFlinger服务停止显示开机动画的。

       在SurfaceFlinger服务,类型为IBinder.FIRST_CALL_TRANSACTION的进程间通信请求被定义为停止显示开机动画的请求,位于frameworks/native/include/gui/ISurfaceComposer.h中:

123456789101112131415
class BnSurfaceComposer : public BnInterface<ISurfaceComposer>  {  public:      enum {          // Note: BOOT_FINISHED must remain this value, it is called from          // Java by ActivityManagerService.          BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,          ......      };        virtual status_t    onTransact( uint32_t code,                                      const Parcel& data,                                      Parcel* reply,                                      uint32_t flags = 0);  };

       BnSurfaceComposer类它是SurfaceFlinger服务所要继承的Binder本地对象类,其中。当SurfaceFlinger服务接收到类型为IBinder::FIRST_CALL_TRANSACTION,即类型为BOOT_FINISHED的进程间通信请求时,它就会将该请求交给它的成员函数bootFinished来处理。

       SurfaceFlinger服务的成员函数bootFinished实现在文件frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp中,如下所示:

12345678910111213141516171819
void SurfaceFlinger::bootFinished(){    const nsecs_t now = systemTime();    const nsecs_t duration = now - mBootTime;    ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );    mBootFinished = true;    // wait patiently for the window manager death    const String16 name("window");    sp<IBinder> window(defaultServiceManager()->getService(name));    if (window != 0) {        window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));    }    // stop boot animation    // formerly we would just kill the process, but we now ask it to exit so it    // can choose where to stop the animation.    property_set("service.bootanim.exit", "1");}

       可以看到,该函数将属性“service.bootanim.exit”设置为”1”。在第2节分析android()代码的时候,我们讲到:当属性“service.bootanim.exit”值被设为”1”时,android()就会退出,开机动画显示自然也就结束了。由于android()退出且返回值为false,BootAnimation::threadLoop()线程也就结束了。再回到BootAnimation.cpp的main()函数中,threadLoop()线程结束,main函数也就结束,至此,bootanimaiton进程就自行结束,开机动画的显示完成了。

       至此,Android系统的三个开机画面的显示过程就分析完成了。

小结

       本文的目的并不是单纯为了介绍Android系统的开机画面,而是希望能够以Android系统的开机画面来作为切入点来分析SurfaceFlinger。后续文章我们会详细分析SurfaceFlinger的每个模块和功能。没图了,等过一阵子再去拍一波~






阅读全文
0 0
原创粉丝点击