Jimmy Chen

A Programmer

(转载)Android开机动画流程简述

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

开机动画的启动

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

bootanimation的启动过程

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

......
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中能看到对该服务的描述:

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

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

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中:

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

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

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中:

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中:

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中,如下所示:

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中,如下所示:

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中的,如下所示:

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中:

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就会被调用:

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类的构造函数中创建的,如下所示:

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的实现如下所示:

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.

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

  • 如果设备的加密功能已经开启,或者设备正在进行加密,则读取加密开机动画文件,路径为:
#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
  • OEM厂商指定的开机动画,路径为:
#define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
  • 系统开机动画,路径为:
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"

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

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

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的实现如下所示:

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;
}

  一共有以下几个步骤:

  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类来访问。

《(转载)Android开机动画流程简述》

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

  图片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被结束为止。

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

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的值:

#define EXIT_PROP_NAME "service.bootanim.exit"

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

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

  回到android()代码:

} while (!exitPending());

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

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:

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,该文件用来描述开机动画如何显示。下面以一个示例来分析一下该文件:

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。
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:

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 ; jgetEntryInfo(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:

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:

<

pre>
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的实现如下所示:

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中,如下所示:

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来处理的,如下所示:

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如下:

// 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方法完成系统启动。我们进入往下看:

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的界面:

void enableScreenAfterBoot() {
    EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
            SystemClock.uptimeMillis());
    mWindowManager.enableScreenAfterBoot();

    synchronized (this) {
        updateEventDispatchingLocked();
    }
}

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

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

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的实现如下所示:

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中:

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中,如下所示:

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的每个模块和功能。

转载自: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/

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d 博主赞过: