android開機動畫播放流程

開機畫面是由應用程序bootanimation來負責顯示的。應用程序bootanimation在啓動腳本init.rc中被配置成了一個服務,如下所示:   

service bootanim /system/bin/bootanimation
    class main
    disabled
    oneshot

應用程序bootanimation的用戶和用戶組名稱分別被設置爲graphics。注意, 用來啓動應用程序bootanimation的服務是disable的,即init進程在啓動的時候,不會主動將應用程序bootanimation啓動起來。當SurfaceFlinger服務啓動的時候,它會通過修改系統屬性ctl.start的值來通知init進程啓動應用程序bootanimation,以便可以顯示第三個開機畫面,而當System進程將系統中的關鍵服務都啓動起來之後,ActivityManagerService服務就會通知SurfaceFlinger服務來修改系統屬性ctl.stop的值,以便可以通知init進程停止執行應用程序bootanimation,即停止顯示第三個開機畫面。接下來我們就分別分析第三個開機畫面的顯示過程和停止過程。

從前面Android系統進程Zygote啓動過程的源代碼分析一文可以知道,Zygote進程在啓動的過程中,會將System進程啓動起來,而從前面Android應用程序安裝過程源代碼分析一文又可以知道,System進程在啓動的過程(Step 3)中,會調用SurfaceFlinger類的靜態成員函數instantiate來啓動SurfaceFlinger服務。Sytem進程在啓動SurfaceFlinger服務的過程中,首先會創建一個SurfaceFlinger實例,然後再將這個實例註冊到Service Manager中去。在註冊的過程,前面創建的SurfaceFlinger實例會被一個sp指針引用。從前面Android系統的智能指針(輕量級指針、強指針和弱指針)的實現原理分析一文可以知道,當一個對象第一次被智能指針引用的時候,這個對象的成員函數onFirstRef就會被調用。由於SurfaceFlinger重寫了父類RefBase的成員函數onFirstRef,因此,在註冊SurfaceFlinger服務的過程中,將會調用SurfaceFlinger類的成員函數onFirstRef。在調用的過程,就會創建一個線程來啓動第三個開機畫面。

SurfaceFlinger類實現在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp 中,它的成員函數onFirstRef的實現如下所示:

void SurfaceFlinger::onFirstRef()
{
    mEventQueue.init(this);

    run("SurfaceFlinger", PRIORITY_URGENT_DISPLAY);

    // Wait for the main thread to be done with its initialization
    mReadyToRunBarrier.wait();
}

SurfaceFlinger類繼承了Thread類,當它的成員函數run被調用的時候,系統就會創建一個新的線程。這個線程在第一次運行之前,會調用SurfaceFlinger類的成員函數readyToRun來通知SurfaceFlinger,它準備就緒了。當這個線程準備就緒之後,它就會循環執行SurfaceFlinger類的成員函數threadLoop,直到這個成員函數的返回值等於false爲止。

注意,SurfaceFlinger類的成員函數onFirstRef是在System進程的主線程中調用的,它需要等待前面創建的線程準備就緒之後,再繼續往前執行,這個通過調用SurfaceFlinger類的成員變量mReadytoRunBarrier所描述的一個Barrier對象的成員函數wait來實現的。每一個Barrier對象內問都封裝了一個條件變量(Condition Variable),而條件變量是用來同步線程的。

接下來,我們繼續分析SurfaceFlinger類的成員函數readyToRun的實現,如下所示:

status_t SurfaceFlinger::readyToRun()
{
    ALOGI(  "SurfaceFlinger's main thread ready to run. "
            "Initializing graphics H/W...");
...........
    // We're now ready to accept clients...
    mReadyToRunBarrier.open();
  // set initial conditions (e.g. unblank default device)
    initializeDisplays();

    // start boot animation
    startBootAnim();

    return NO_ERROR;
}
void SurfaceFlinger::startBootAnim() {
    // start boot animation
    mBootFinished = false;
    property_set("service.bootanim.exit", "0");
    property_set("ctl.start", "bootanim");
}

前面創建的線程用作SurfaceFlinger的主線程。這個線程在啓動的時候,會對設備主屏幕以及OpenGL庫進行初始化。初始化完成之後,接着就會調用SurfaceFlinger類的成員變量mReadyToRunBarrier所描述的一個Barrier對象的成員函數open來喚醒System進程的主線程,以便它可以繼續往前執行。最後,SurfaceFlinger類的成員函數readyToRun的成員函數會調用函數property_set來將系統屬性“ctl.start”的值設置爲“bootanim”,表示要將應用程序bootanimation啓動起來,以便可以顯示開機畫面。

當系統屬性發生改變時,init進程就會接收到一個系統屬性變化通知,這個通知最終是由在init進程中的函數handle_property_set_fd來處理的。

函數handle_property_set_fd實現在文件system/core/init/property_service.c中,如下所示:

void handle_property_set_fd()
{
    prop_msg msg;
    int s;
    int r;
    int res;
    struct ucred cr;
    struct sockaddr_un addr;
    socklen_t addr_size = sizeof(addr);
    socklen_t cr_size = sizeof(cr);
    char * source_ctx = NULL;

    if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
        return;
    }

    /* Check socket options here */
    if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
        close(s);
        ERROR("Unable to receive socket options\n");
        return;
    }
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));
    if(r != sizeof(prop_msg)) {
        ERROR("sys_prop: mis-match msg size received: %d expected: %d 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;

        getpeercon(s, &source_ctx);
  if(memcmp(msg.name,"ctl.",4) == 0) {
            // Keep the old close-socket-early behavior when handling
            // ctl.* properties.
            close(s);
            if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) {
                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 {
 if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) {
                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);
    } else if (!strcmp(msg,"stop")) {
        msg_stop(arg);
    } else if (!strcmp(msg,"restart")) {
        msg_stop(arg);
        msg_start(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)
{
    struct service *svc;
    char *tmp = NULL;
    char *args = NULL;

    if (!strchr(name, ':'))
        svc = service_find_by_name(name);
    else {
        tmp = strdup(name);
        args = strchr(tmp, ':');
        *args = '\0';
        args++;

        svc = service_find_by_name(tmp);
    }

    if (svc) {
        service_start(svc, args);
    } else {
        ERROR("no such service '%s'\n", name);
    }
    if (tmp)
        free(tmp);
}

參數name的值等於“bootanim”,它用來描述一個服務名稱。這個函數首先調用函數service_find_by_name來找到名稱等於“bootanim”的服務的信息,這些信息保存在一個service結構體svc中,接着再調用另外一個函數service_start來將對應的應用程序啓動起來。

從前面的內容可以知道,名稱等於“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("persist.env.sys.bootanim", value, "true");
    int noBootAnimation = 0;
    if (strcmp(value, "false") == 0)
    {
        noBootAnimation = 1;
    }
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) {

        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();

        // create the boot animation object
        sp<BootAnimation> boot = new BootAnimation();

        IPCThreadState::self()->joinThreadPool();

    }
    return 0;
}

這個函數首先檢查系統屬性“persist.env.sys.bootanim”的值是否不等於false。如果不等於的話,那麼接下來就會啓動一個Binder線程池,並且創建一個BootAnimation對象。這個BootAnimation對象就是用來顯示第三個開機畫面的。由於BootAnimation對象在顯示第三個開機畫面的過程中,需要與SurfaceFlinger服務通信,因此,應用程序bootanimation就需要啓動一個Binder線程池。

BootAnimation類間接地繼承了RefBase類,並且重寫了RefBase類的成員函數onFirstRef,因此,當一個BootAnimation對象第一次被智能指針引用的時,這個BootAnimation對象的成員函數onFirstRef就會被調用。

BootAnimation類的成員函數onFirstRef實現在文件frameworks/base/cmds/bootanimation/BootAnimation.cpp中,如下所示:

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)
{
    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;
    char value[PROPERTY_VALUE_MAX];
    property_get("persist.panel.orientation", value, "0");
    int orient = atoi(value) / 90;

    if(orient == eOrientation90 || orient == eOrientation270) {
        int temp = dinfo.h;
        dinfo.h = dinfo.w;
        dinfo.w = temp;
    }
 Rect destRect(dinfo.w, dinfo.h);
    mSession->setDisplayProjection(dtoken, orient, destRect, destRect);

    // create the native surface
    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);

    SurfaceComposerClient::openGlobalTransaction();
    control->setLayer(0x40000000);
    SurfaceComposerClient::closeGlobalTransaction();

    sp<Surface> s = control->getSurface();

    // initialize opengl and 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;

    mAndroidAnimation = true;

    // 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);
    if ((encryptedAnimation &&
            (access(getAnimationFileName(IMG_ENC), R_OK) == 0) &&
            (mZip.open(getAnimationFileName(IMG_ENC)) == NO_ERROR)) ||

            ((access(getAnimationFileName(IMG_DATA), R_OK) == 0) &&
            (mZip.open(getAnimationFileName(IMG_DATA)) == NO_ERROR)) ||

            ((access(getAnimationFileName(IMG_SYS), R_OK) == 0) &&
            (mZip.open(getAnimationFileName(IMG_SYS)) == NO_ERROR))) {
        mAndroidAnimation = false;
    }
  return NO_ERROR;
}

BootAnimation類的成員函數session用來返回BootAnimation類的成員變量mSession所描述的一個SurfaceComposerClient對象。通過調用SurfaceComposerClient對象mSession的成員函數createSurface可以獲得一個SurfaceControl對象control。

SurfaceComposerClient類的成員函數createSurface首先調用內部的Binder代理對象mClient來請求SurfaceFlinger返回一個類型爲SurfaceLayer的Binder代理對象,接着再使用這個Binder代理對象來創建一個SurfaceControl對象。創建出來的SurfaceControl對象的成員變量mSurface就指向了從SurfaceFlinger返回來的類型爲SurfaceLayer的Binder代理對象。有了這個Binder代理對象之後,SurfaceControl對象就可以和SurfaceFlinger服務通信了。

調用SurfaceControl對象control的成員函數getSurface會返回一個Surface對象s。這個Surface對象s內部也有一個類型爲SurfaceLayer的Binder代理對象mSurface,這個Binder代理對象與前面所創建的SurfaceControl對象control的內部的Binder代理對象mSurface引用的是同一個SurfaceLayer對象。這樣,Surface對象s也可以通過其內部的Binder代理對象mSurface來和SurfaceFlinger服務通信。

Surface類繼承了ANativeWindow類。ANativeWindow類是連接OpenGL和Android窗口系統的橋樑,即OpenGL需要通過ANativeWindow類來間接地操作Android窗口系統。這種橋樑關係是通過EGL庫來建立的,所有以egl爲前綴的函數名均爲EGL庫提供的接口。

爲了能夠在OpenGL和Android窗口系統之間的建立一個橋樑,我們需要一個EGLDisplay對象display,一個EGLConfig對象config,一個EGLSurface對象surface,以及一個EGLContext對象context,其中,EGLDisplay對象display用來描述一個EGL顯示屏,EGLConfig對象config用來描述一個EGL幀緩衝區配置參數,EGLSurface對象surface用來描述一個EGL繪圖表面,EGLContext對象context用來描述一個EGL繪圖上下文(狀態),它們是分別通過調用egl庫函數eglGetDisplay、EGLUtils::selectConfigForNativeWindow、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之後,就可以通過其內部的Binder代理對象mSurface來請求SurfaceFlinger服務返回幀緩衝區硬件設備的一個圖形訪問接口。這樣,OpenGL最終就可以將要繪製的圖形渲染到幀緩衝區硬件設備中去,即顯示在實際屏幕上。屏幕的大小,即寬度和高度,可以通過函數eglQuerySurface來獲得。

BootAnimation類的成員變量mAndroidAnimation是一個布爾變量。當它的值等於true的時候,那麼就說明需要顯示的第三個開機畫面是Android系統默認的開機動畫,否則的話,第三個開機畫面就是由用戶自定義的開機動畫。

自定義的開機動畫是由文件USER_BOOTANIMATION_FILE或者文件SYSTEM_BOOTANIMATION_FILE來描述的。只要其中的一個文件存在,那麼第三個開機畫面就會使用用戶自定義的開機動畫。USER_BOOTANIMATION_FILE和SYSTEM_BOOTANIMATION_FILE均是一個宏,它們的定義如下所示:

#define USER_BOOTANIMATION_FILE "/data/boot/bootanimation.zip"
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"

這一步執行完成之後,用來顯示開機畫面的線程的初始化工作就執行完成了,接下來,就會執行這個線程的主體函數,即BootAnimation類的成員函數threadLoop。

BootAnimation類的成員函數threadLoop的實現如下所示:


bool BootAnimation::threadLoop()
{
    bool r;
    if (mAndroidAnimation) {
        r = android();
    } else {
        r = movie();
    }

    // No need to force exit anymore
    property_set(EXIT_PROP_NAME, "0");

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

bool BootAnimation::threadLoop()
{
    bool r;
    if (mAndroidAnimation) {
        r = android();
    } else {
        r = movie();
    }

    // No need to force exit anymore
    property_set(EXIT_PROP_NAME, "0");

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

如果BootAnimation類的成員變量mAndroidAnimation的值等於true,那麼接下來就會調用BootAnimation類的成員函數android來顯示系統默認的開機動畫,否則的話,就會調用BootAnimation類的成員函數movie來顯示用戶自定義的開機動畫。顯示完成之後,就會銷燬前面所創建的EGLContext對象mContext、EGLSurface對象mSurface,以及EGLDisplay對象mDisplay等。

接下來,我們就分別分析BootAnimation類的成員函數android和movie的實現。

BootAnimation類的成員函數android的實現如下所示:

bool BootAnimation::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);
        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();
    } 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類來訪問。

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-shine.png都作爲一個整體來渲染,而且它的位置是恆定不變的。由於它是一個鏤空的“ANDROID”圖像,因此,我們就可以通過它的鏤空來看到它背後的圖片android-logo-shine.png的條紋一閃一閃地劃過。

這個while循環語句會一直被執行,直到應用程序/system/bin/bootanimation被結束爲止,後面我們再分析。

BootAnimation類的成員函數movie的實現比較長,我們分段來閱讀:

bool BootAnimation::movie()
{
    char value[PROPERTY_VALUE_MAX];
    ZipFileRO& zip(mZip);

    size_t numEntries = zip.getNumEntries();
    ZipEntryRO desc = zip.findEntryByName("desc.txt");
    FileMap* descMap = zip.createEntryFileMap(desc);
    ALOGE_IF(!descMap, "descMap is null");
    if (!descMap) {
        return false;
    }

    String8 desString((char const*)descMap->getDataPtr(),
            descMap->getDataLength());
    char const* s = desString.string();

    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[256];
        char pathType;
        if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) {
            //LOGD("> 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", &pathType, &count, &pause, path) == 4) {
            //LOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path);
            Animation::Part part;
            part.playUntilComplete = pathType == 'c';
            part.count = count;
            part.pause = pause;
            part.path = path;
            animation.parts.add(part);
        }

        s = ++endl;
}

從前面BootAnimation類的成員函數readyToRun的實現可以知道,如果目標設備上存在壓縮文件/data/local/bootanimation.zip,那麼BootAnimation類的成員變量mZip就會指向它,否則的話,就會指向目標設備上的壓縮文件/system/media/bootanimation.zip。無論BootAnimation類的成員變量mZip指向的是哪一個壓縮文件,這個壓縮文件都必須包含有一個名稱爲“desc.txt”的文件,用來描述用戶自定義的開機動畫是如何顯示的。

文件desc.txt的內容格式如下面的例子所示:

720 1280 12
    p 1 0 part0
    p 0 0 part1

第一行的三個數字分別表示開機動畫在屏幕中的顯示寬度、高度以及幀速(fps)。剩餘的每一行都用來描述一個動畫片斷,這些行必須要以字符“p”來開頭,後面緊跟着兩個數字以及一個文件目錄路徑名稱。第一個數字表示一個片斷的循環顯示次數,如果它的值等於0,那麼就表示無限循環地顯示該動畫片斷。第二個數字表示每一個片斷在兩次循環顯示之間的時間間隔。這個時間間隔是以一個幀的時間爲單位的。文件目錄下面保存的是一系列png文件,這些png文件會被依次顯示在屏幕中。

以上面這個desct.txt文件的內容爲例,它描述了一個大小爲600 x 480的開機動畫,動畫的顯示速度爲24幀每秒。這個開機動畫包含有兩個片斷part1和part2。片斷part1只顯示一次,它對應的png圖片保存在目錄part1中。片斷part2無限循環地顯示,其中,每兩次循環顯示的時間間隔爲10 x (1 / 24)秒,它對應的png圖片保存在目錄part2中。

上面的for循環語句分析完成desc.txt文件的內容後,就得到了開機動畫的顯示大小、速度以及片斷信息。這些信息都保存在Animation對象animation中,其中,每一個動畫片斷都使用一個Animation::Part對象來描述,並且保存在Animation對象animation的成員變量parts所描述的一個片斷列表中。

接下來,BootAnimation類的成員函數movie再斷續將每一個片斷所對應的png圖片讀取出來,如下所示:

    // read all the data structures
    const size_t pcount = animation.parts.size();
    for (size_t i=0 ; i<numEntries ; i++) {
        char name[256];
        ZipEntryRO entry = zip.findEntryByIndex(i);
        if (zip.getEntryFileName(entry, name, 256) == 0) {
            const String8 entryName(name);
            const String8 path(entryName.getPathDir());
            const String8 leaf(entryName.getPathLeaf());
            if (leaf.size() > 0) {
                for (int j=0 ; j<pcount ; j++) {
                    if (path == animation.parts[j].path) {
                        int method;
                        // supports only stored png files
                        if (zip.getEntryInfo(entry, &method, 0, 0, 0, 0, 0)) {
                            if (method == ZipFileRO::kCompressStored) {
                                FileMap* map = zip.createEntryFileMap(entry);
 if (map) {
                                    Animation::Frame frame;
                                    frame.name = leaf;
                                    frame.map = map;
                                    Animation::Part& part(animation.parts.editItemAt(j));
                                    part.frames.add(frame);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

每一個png圖片都表示一個動畫幀,使用一個Animation::Frame對象來描述,並且保存在對應的Animation::Part對象的成員變量frames所描述的一個幀列表中。

獲得了開機動畫的所有信息之後,接下來BootAnimation類的成員函數movie就準備開始顯示開機動畫了,如下所示:

 // 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用來描述屏幕中除了開機動畫之外的其它區域,它是用整個屏幕區域減去開機動畫所點據的區域來得到的。

準備好開機動畫的顯示參數之後,最後就可以執行顯示開機動畫的操作了,如下所示:

  for (int i=0 ; i<pcount && !exitPending() ; i++) {
        const Animation::Part& part(animation.parts[i]);
        const size_t fcount = part.frames.size();
        glBindTexture(GL_TEXTURE_2D, 0);

        for (int r=0 ; !part.count || r<part.count ; r++) {
            // Exit any non playuntil complete parts immediately
            if(exitPending() && !part.playUntilComplete)
                break;

            for (int j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
                const Animation::Frame& frame(part.frames[j]);
                nsecs_t lastFrame = systemTime();
    {
                    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.map->getDataPtr(),
                            frame.map->getDataLength());
                }

                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;
    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);
                }
                if (part.count != 1) {
                    glDeleteTextures(1, &frame.tid);
                }
                checkExit();
            }

            usleep(part.pause * ns2us(frameDuration));
          // For infinite parts, we've now played them at least once, so perhaps exit
            if(exitPending() && !part.count)
                break;
        }
}

第一層for循環用來顯示每一個動畫片斷,第二層的for循環用來循環顯示每一個動畫片斷,第三層的for循環用來顯示每一個動畫片斷所對應的png圖片。這些png圖片以紋理的方式來顯示在屏幕中。

注意,如果一個動畫片斷的循環顯示次數不等於1,那麼就說明這個動畫片斷中的png圖片需要重複地顯示在屏幕中。由於每一個png圖片都需要轉換爲一個紋理對象之後才能顯示在屏幕中,因此,爲了避免重複地爲同一個png圖片創建紋理對象,第三層的for循環在第一次顯示一個png圖片的時候,會調用函數glGenTextures來爲這個png圖片創建一個紋理對象,並且將這個紋理對象的名稱保存在對應的Animation::Frame對象的成員變量tid中,這樣,下次再顯示相同的圖片時,就可以使用前面已經創建好了的紋理對象,即調用函數glBindTexture來指定當前要操作的紋理對象。

如果Region對象clearReg所包含的區域不爲空,那麼在調用函數glDrawTexiOES和eglSwapBuffers來顯示每一個png圖片之前,首先要將它所包含的區域裁剪掉,避免開機動畫可以顯示在指定的位置以及大小中。

每當顯示完成一個png圖片之後,都要將變量frameDuration的值從納秒轉換爲毫秒。如果轉換後的值大小於,那麼就需要調用函數usleep函數來讓線程睡眠一下,以保證每一個png圖片,即每一幀動畫都按照預先指定好的速度來顯示。注意,函數usleep指定的睡眠時間只能精確到毫秒,因此,如果預先指定的幀顯示時間小於1毫秒,那麼BootAnimation類的成員函數movie是無法精確地控制地每一幀的顯示時間的。

還有另外一個地方需要注意的是,每當循環顯示完成一個片斷時,需要調用usleep函數來使得線程睡眠part.pause * ns2us(frameDuration)毫秒,以便可以按照預先設定的節奏來顯示開機動畫。

最後一個if語句判斷一個動畫片斷是否是循環顯示的,即循環次數不等於1。如果是的話,那麼就說明前面爲它所對應的每一個png圖片都創建過一個紋理對象。現在既然這個片斷的顯示過程已經結束了,因此,就需要釋放前面爲它所創建的紋理對象。

至此,開機畫面的顯示過程就分析完成了。

接下來,我們再繼續分析開機畫面是如何停止顯示的。

從前面Android系統默認Home應用程序(Launcher)的啓動過程源代碼分析一文可以知道,當System進程將系統中的關鍵服務啓動起來之後,就會將應用程序啓動器(Launcher)啓動起來。從Android應用程序啓動過程源代碼分析一文又可以知道,Android應用程序的啓動過程實際上就是它的根Activity組件的啓動過程。對於應用程序Launcher來說,它的根Activity組件即爲Launcher組件。

一個Activity組件在啓動起來之後,就會被記錄起來,等到它所運行在的主線程空閒的時候,這個主線程就會向ActivityManagerService發送一個Activity組件空閒的通知。由於應用程序Launcher是系統中第一個被啓動的應用程序,即它的根Activity組件是系統中第一個被啓動的Activity組件,因此,當ActivityManagerService接收到它的空閒通知的時候,就可以知道系統是剛剛啓動起來的。在這種情況下,ActivityManagerService就會停止顯示開機動畫,以便可以在屏幕中顯示應用程序Lancher的界面。

從前面Android應用程序消息處理機制(Looper、Handler)分析一文可以知道,如果一個線程想要在空閒的時候處理一些事務,那麼就必須要向這個線程的消息隊列註冊一個空閒消息處理器。自定義的空閒消息處理器燈必須要從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的實現如下所示:

public final class ActivityThread {
  .............
 private class Idler implements MessageQueue.IdleHandler {
        public final boolean queueIdle() {
            ActivityClientRecord a = mNewActivities;
            boolean stopProfiling = false;
            if (mBoundApplication != null && mProfiler.profileFd != null
                    && mProfiler.autoStopProfiler) {
                stopProfiling = true;
            }
            if (a != null) {
                mNewActivities = null;
                IActivityManager am = ActivityManagerNative.getDefault();
                ActivityClientRecord prev;
                do {
   if (localLOGV) Slog.v(
                        TAG, "Reporting idle of " + a +
                        " finished=" +
                        (a.activity != null && a.activity.mFinished));
                    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);
  }
            if (stopProfiling) {
                mProfiler.stopProfiling();
            }
            ensureJitEnabled();
            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, boolean stopProfiling)
            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);
        }
        data.writeInt(stopProfiling ? 1 : 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 void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        final long origId = Binder.clearCallingIdentity();
        ActivityRecord r = mMainStack.activityIdleInternal(token, false, config);
        if (stopProfiling) {
            synchronized (this) {
                if (mProfileProc == r.app) {
                    if (mProfileFd != null) {
                        try {
                            mProfileFd.close();
                        } catch (IOException e) {
                        }
                        clearProfilerLocked();
                    }
                }
            }
        }
        Binder.restoreCallingIdentity(origId);
    }

ActivityManagerService類有一個類型爲ActivityStack的成員變量mMainStack,它用來描述系統的Activity組件堆棧,它的成員函數activityIdleInternal的實現如下所示:

public class ActivityStack {
    ......

    final void activityIdleInternal(IBinder token, boolean fromTimeout,
            Configuration config) {
        ......

        boolean enableScreen = false;

        synchronized (mService) {
            ......

            // Get the activity record.
            int index = indexOfTokenLocked(token);
            if (index >= 0) {
                ActivityRecord r = (ActivityRecord)mHistory.get(index);                
                ......

                if (mMainStack) {
                    if (!mService.mBooted && !fromTimeout) {
                        mService.mBooted = true;
                        enableScreen = true;
                    }
                }
            }

            ......
        }

        ......

        if (enableScreen) {
            mService.enableScreenAfterBoot();
        }
    }

    ......
}         

參數token用來描述剛剛啓動起來的Launcher組件,通過它來調用函數indexOfTokenLocked就可以得到Launcher組件在系統Activity組件堆棧中的位置index。得到了Launcher組件在系統Activity組件堆棧中的位置index之後,就可以在ActivityStack類的成員變量mHistory中得到一個ActivityRecord對象r。這個ActivityRecord對象r同樣是用來描述Launcher組件的。

ActivityStack類的成員變量mMainStack是一個布爾變量,當它的值等於true的時候,就說明當前正在處理的ActivityStack對象是用來描述系統的Activity組件堆棧的。 ActivityStack類的另外一個成員變量mService指向了系統中的ActivityManagerService服務。ActivityManagerService服務有一個類型爲布爾值的成員變量mBooted,它的初始值爲false,表示系統尚未啓動完成。

從前面的調用過程可以知道,參數fromTimeout的值等於false。在這種情況下,如果ActivityManagerService服務的成員變量mBooted也等於false,那麼就說明應用程序已經啓動起來了,即說明系統已經啓動完成了。這時候ActivityManagerService服務的成員變量mBooted以及變量enableScreen的值就會被設置爲true。

當變量enableScreen的值等於true的時候,ActivityStack類就會調用ActivityManagerService服務的成員函數enableScreenAfterBoot停止顯示開機動畫,以便可以將屏幕讓出來顯示應用程序Launcher的界面。

ActivityManagerService類的成員函數enableScreenAfterBoot的實現如下所示:

public final class ActivityManagerService extends ActivityManagerNative
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
    ......

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

    ......
}

ActivityManagerService類的成員變量mWindowManager指向了系統中的Window管理服務WindowManagerService,ActivityManagerService服務通過調用它的成員函數enableScreenAfterBoot來停止顯示開機動畫。

WindowManagerService類的成員函數enableScreenAfterBoot的實現如下所示:

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor {
    ......

    public void enableScreenAfterBoot() {
        synchronized(mWindowMap) {
            if (mSystemBooted) {
                return;
            }
            mSystemBooted = true;
        }

        performEnableScreen();
    }

    ......
} 

WindowManagerService類的成員變量mSystemBooted用來記錄系統是否已經啓動完成的。如果已經啓動完成的話,那麼這個成員變量的值就會等於true,這時候WindowManagerService類的成員函數enableScreenAfterBoot什麼也不做就返回了,否則的話,WindowManagerService類的成員函數enableScreenAfterBoot首先將這個成員變量的值設置爲true,接着再調用另外一個成員函數performEnableScreen來執行停止顯示開機動畫的操作。

WindowManagerService類的成員函數performEnableScreen的實現如下所示:

public class WindowManagerService extends IWindowManager.Stub
        implements Watchdog.Monitor {
    ......

    public void performEnableScreen() {
        synchronized(mWindowMap) {
            if (mDisplayEnabled) {
                return;
            }
            if (!mSystemBooted) {
                return;
            }

            ......

            mDisplayEnabled = true;
            ......

            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,
                                            data, null, 0);
                    data.recycle();
                }
            } catch (RemoteException ex) {
                Slog.e(TAG, "Boot completed: SurfaceFlinger is dead!");
            }
        }

        ......
    }

    ......
}  

WindowManagerService類的另外一個成員變量mDisplayEnabled用來描述WindowManagerService是否已經初始化過系統的屏幕了,只有當它的值等於false,並且系統已經完成啓動,即WindowManagerService類的成員變量mSystemBooted等於true的情況下,WindowManagerService類的成員函數performEnableScreen才通知SurfaceFlinger服務停止顯示開機動畫。

注意,WindowManagerService類的成員函數performEnableScreen是通過一個類型爲IBinder.FIRST_CALL_TRANSACTION的進程間通信請求來通知SurfaceFlinger服務停止顯示開機動畫的。

在SurfaceFlinger服務,類型爲IBinder.FIRST_CALL_TRANSACTION的進程間通信請求被定義爲停止顯示開機動畫的請求,如下所示:

class BnSurfaceComposer : public BnInterface
{
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類定義在文件frameworks/base/include/surfaceflinger/ISurfaceComposer.h中,它是SurfaceFlinger服務所要繼承的Binder本地對象類,其中。當SurfaceFlinger服務接收到類型爲IBinder::FIRST_CALL_TRANSACTION,即類型爲BOOT_FINISHED的進程間通信請求時,它就會將該請求交給它的成員函數bootFinished來處理。

SurfaceFlinger服務的成員函數bootFinished實現在文件frameworks/base/services/surfaceflinger/SurfaceFlinger.cpp中,如下所示:

void SurfaceFlinger::bootFinished()
{
    const nsecs_t now = systemTime();
    const nsecs_t duration = now - mBootTime;
    LOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
    mBootFinished = true;
    property_set("ctl.stop", "bootanim");
} 

這個函數主要就是將系統屬性“ctl.stop”的值設置爲“bootanim”。前面提到,每當有一個系統屬性發生變化時,init進程就會被喚醒,並且調用運行在它裏面的函數handle_property_set_fd來處理這個系統屬性變化事件。在我們這個場景中,由於被改變的系統屬性的名稱是以"ctl."開頭的,即被改變的系統屬性是一個控制類型的屬性,因此,接下來函數handle_property_set_fd又會調用另外一個函數handle_control_message來處理該系統屬性變化事件。

函數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);
    } else if (!strcmp(msg,"stop")) {
        msg_stop(arg);
    } else {
        ERROR("unknown control msg '%s'\n", msg);
    }
} 

從前面的調用過程可以知道,參數msg和arg的值分別等於"stop"和“bootanim”,這表示要停止執行名稱爲“bootanim”的服務,這是通過調用函數msg_stop來實現的。

函數msg_stop也是實現在文件system/core/init/init.c中,如下所示:

static void msg_stop(const char *name)
{
    struct service *svc = service_find_by_name(name);

    if (svc) {
        service_stop(svc);
    } else {
        ERROR("no such service '%s'\n", name);
    }
} 

這個函數首先調用函數service_find_by_name來找到名稱等於name,即“bootanim”的服務,然後再調用函數service_stop來停止這個服務。

前面提到,名稱爲“bootanim”的服務對應的應用程序即爲/system/bin/bootanimation。因此,停止名稱爲“bootanim”的服務即爲停止執行應用程序/system/bin/bootanimation,而當應用程序/system/bin/bootanimation停止執行的時候,開機動畫就會停止顯示了。

至此,Android系統的開機畫面的顯示過程就分析完成了.

轉載地址:http://www.uml.org.cn/mobiledev/201209052.asp





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章