"近期有个项目需要修改开机动画,便网上搜罗了各种资料,对比源码进行分析,整理了一下开机动画的显示流程。这里的开机动画是指在android层的开机动画。至于mboot层和linex kernel层的Logo显示流程就不深究了。 先放张图看一下整体的流程: [图片] 先由 init 进程启动 Surfac ...."

android 开机动画流程

近期有个项目需要修改开机动画,便网上搜罗了各种资料,对比源码进行分析,整理了一下开机动画的显示流程。这里的开机动画是指在android层的开机动画。至于mboot层和linex kernel层的Logo显示流程就不深究了。

先放张图看一下整体的流程:
b1098c5d1ccb440fa266ccef27c84655_20141231162020380.png

先由 init 进程启动 SurfaceFlinger 进程,对图形系统进行初始化,再由 SurfaceFlinger 进程启动 BootAnimation 进程显示动画。动画由一个 while 循环播放,在播放循环里调用 checkExit 检查一个系统标志,判断是否应该结束动画。 动画的显示的流程倒是挺简洁的,但动画的结束标志设置却是非常复杂。经过 Luancher ,ActivityManagerService,SurfaceFlinger。。。层层调用,传递消息,最终在SurfaceFlinger.bootFinished 设置动画结束标志的。接下来我们详细的分析一个整个流程。

首先从init进程开始,init进程是linux内核启动后,启动的第一个进程,init 会解析init.rc 文件,启动其中声明的服务。在init.rc文件中有这样的配置:

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

即init会启动surfaceflinger服务。其相关代码在frameworks/native/services/surfaceflinger目录下,入口文件为main_surfaceflinger.cpp:

#include "SurfaceFlinger.h"

using namespace android;

int main(int argc, char** argv) {
    // 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();
    ......
    // 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对象flinger,然后调用flinger->init();进行初始化,再将其加入ServiceManager中进行管理,最后启动服务。我们来看一下 init()函数:

void SurfaceFlinger::init() {
    ......
    mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(mEGLDisplay, NULL, NULL);
    // start boot animation
    startBootAnim();
}

init()函数前面主要是对屏幕显示窗口进行初始化工作,具体不作深入分析,我们的目标是开机动画相关的内容。最后一行调用了startBootAnim();

void SurfaceFlinger::startBootAnim() {
    // start boot animation
    property_set("service.bootanim.exit", "0");
    char path[255];
    property_get("ro.third.bootvideo.path", path,"1");
    ALOGE("ro.third.bootvideo.path: path=%s", path);
     if(access(path, R_OK) != 0) {
        property_set("ctl.start", "bootanim");
        ALOGE("enter bootanima: path=%s", path);
    }else{
     ALOGE("do not enter  bootanima: path=%s", path);
    }
}

关键代码只有两行:

property_set("service.bootanim.exit", "0");
......
property_set("ctl.start", "bootanim");

先将"service.bootanim.exit"标志为 0,这个标志会在bootanim进程中用于检测开机动画是否结束。然后通过发"ctl.start"请求给init进程,启动"bootanim"服务。其中的启动流程可以参考 Android 属性系统 Property service 设定分析

你可能会奇怪,开机动画进程不是在init.rc里面注册了吗?为什么不是init解析init.rc文件时就直接启动呢?而是由SurfaceFlinger服务来启动呢?

service bootanim /system/bin/bootanimation
    class main
    user root
    group graphics
    disabled
    oneshot

init.rc 虽然注册了bootanimation服务,但是被disabled了。所以在解析init.rc文件时,bootanimation并没有被直接启动。由SurfaceFlinger启动的原因是开机动画必须得有显示窗口,而这个显示窗口则是由SurfaceFlinger提供的,所以必须得在SurfaceFlinger初始化完成之后,才开始开机动画。下面流程转到bootanimation。

bootanimation相关的代码在 frameworks/base/cmds/bootanimation 目录下,入口文件为 bootanimation_main.cpp :

#include "BootAnimation.h"
using namespace android;
int main(int argc, char** argv)
{
    ...
        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();

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

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

启动了一个线程池,由于BootAnimation对象在显示第三个开机画面的过程中,需要与SurfaceFlinger服务通信,因此,应用程序bootanimation就需要启动一个Binder线程池。
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);
    }
}

BootAnimation类继承了Thread类,Thread 类有个run 函数,在run方法之中会调用_threadLoop函数,而_threadLoop函数又调用了readyToRunthreadLoop两个函数:

// file: system/core/libutils/Threads.cpp   
// function: _threadLoop()
     do {
        bool result;
        if (first) {
            first = false;
            self->mStatus = self->readyToRun();
            result = (self->mStatus == NO_ERROR);

            if (result && !self->exitPending()) {
                result = self->threadLoop();
            }   
        } else {
            result = self->threadLoop();
        }

BootAnimation类重写了readyToRunthreadLoop这两个函数:

 status_t BootAnimation::readyToRun() {

    // initialize opengl and egl
    ...

    // 判断是否有开机动画文件 bootanimation.zip 存在
    if ((encryptedAnimation &&
            (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
            (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) ||

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

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

            ((access(path2, R_OK) == 0) &&
            ((mZip.open(path2)) == NO_ERROR)) ||
            ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
            (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) {
        mAndroidAnimation = false;
    }

    return NO_ERROR;
}
bool BootAnimation::threadLoop()
{
    bool r;
    system("echo stop > /proc/msp/mce");
    if (mAndroidAnimation) {
        r = android();
    } else {
        r = movie();
    ALOGE("open movie over r :%d",r);
    }
    ...
}

readyToRun 主要是对opengl工作环境进行初始化,及判断用户自定义的开机动画文件是否存在,保存结果到mAndroidAnimation成员变量中。threadLoop 就开始真正的播放动画了。当mAndroidAnimation的值等于true的时候,则播放Android系统默认的开机动画,否则播放用户自定义的开机动画。

android() 播放的是系统原生动画,“android”字样加上不断移动的光影效果。movie() 则是读取bootanimation.zip 中的帧动画,一张一张的轮播,形成动画效果。下面来看一下原生动画的实现。

bool BootAnimation::android()
{
    initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
    initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");

    ...
    do {
        ...
        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*2);

        checkExit();
    } while (!exitPending());

    glDeleteTextures(1, &mAndroid[0].name);
    glDeleteTextures(1, &mAndroid[1].name);
    return false;
}

使用一个while循环去绘图,具体的绘图过程就不分析了,这个需要对opengl es 有所了解。在循环中调用 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();
    }
}

检测到 "service.bootanim.exit" 的值被修改成非 0 之后,就调用 requestExit() 结束动画。requestExit() 函数是父类 Thread 中定义的:

void Thread::requestExit()
{
    Mutex::Autolock _l(mLock);
    mExitPending = true;
}

bool Thread::exitPending() const
{
    Mutex::Autolock _l(mLock);
    return mExitPending;
}

设置了一个标志位 mExitPending = true,表明准备退出线程。while (!exitPending()) 循环条件就变为 false了,退出动画线程。movie() 的流程也是差不多的,就不分析了。

现在的问题是到底是谁设置了 "service.bootanim.exit" 标志呢?从我们前面分析的代码来看都只是检测而已。茫茫源码,无处入手啊。搜索一下便知道了,到android 源码framework目录下执行:

xushaoqiu@ubuntu:~/svn/HS_V500/system/android4.4/frameworks$ grep -nr "service.bootanim.exit" *
native/services/surfaceflinger/SurfaceFlinger.cpp:312:    property_set("service.bootanim.exit", "1");
native/services/surfaceflinger/SurfaceFlinger.cpp:647:    property_set("service.bootanim.exit", "0");
xushaoqiu@ubuntu:~/svn/HS_V500/system/android4.4/frameworks$

发现只有在 SurfaceFlinger.cpp 文件中将这个标志设置为了 “1”:

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

那是谁调用了SurfaceFlinger::bootFinished 函数呢?继续搜索:

xushaoqiu@ubuntu:~/svn/HS_V500/system/android4.4/frameworks$  grep -nr "bootFinished()" *
native/services/surfaceflinger/SurfaceFlinger.h:210:    virtual void bootFinished();
native/services/surfaceflinger/SurfaceFlinger.cpp:516:void SurfaceFlinger::bootFinished()
native/libs/gui/ISurfaceComposer.cpp:148:    virtual void bootFinished()
native/libs/gui/ISurfaceComposer.cpp:326:            bootFinished();
native/include/gui/ISurfaceComposer.h:105:    virtual void bootFinished() = 0;
xushaoqiu@ubuntu:~/svn/HS_V500/system/android4.4/frameworks$

发现只有在 native/libs/gui/ISurfaceComposer.cpp 文件中有调用:

status_t BnSurfaceComposer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        ...
        case BOOT_FINISHED: {
            CHECK_INTERFACE(ISurfaceComposer, data, reply);
            bootFinished();
            return NO_ERROR;
        }
        ...

BnSurfaceComposer 这个类也有个bootFinished函数:

    virtual void bootFinished()
    {
        Parcel data, reply;
        data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
        remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
    }

但其实这个函数并没有什么用,因为SurfaceFlinger 类继承了BnSurfaceComposer 类,并重写了bootFinished函数,也就是上面我们提到的真正设置property_set("service.bootanim.exit", "1");的函数。所以在 onTransact 中调用的bootFinished 实际上是SurfaceFlinger 中的bootFinished 函数。

我们继续搜索,看看是谁发了 BOOT_FINISHED 消息给SurfaceFlinger 服务。

xushaoqiu@ubuntu:~/svn/HS_V500/system/android4.4/frameworks$ grep -nr "BOOT_FINISHED" 
base/services/java/com/android/server/wm/WindowManagerService.java:5375:                    surfaceFlinger.transact(IBinder.FIRSTCALL_TRANSACTION, // BOOT_FINISHED
native/services/surfaceflinger/SurfaceFlinger.cpp:2998:        case BOOT_FINISHED:
native/services/surfaceflinger/rtd298x_tv035/SurfaceFlinger.cpp:2969:        case BOOT_FINISHED:
native/libs/gui/ISurfaceComposer.cpp:152:        remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
native/libs/gui/ISurfaceComposer.cpp:324:        case BOOT_FINISHED: {
native/include/gui/ISurfaceComposer.h:140:        // Note: BOOT_FINISHED must remain this value, it is called from
native/include/gui/ISurfaceComposer.h:142:        BOOT_FINISHED = IBinder::FIRSTCALL_TRANSACTION,
xushaoqiu@ubuntu:~/svn/HS_V500/system/android4.4/frameworks$

可以发现在 native/include/gui/ISurfaceComposer.h 中有定义如下:

BOOT_FINISHED = IBinder::FIRSTCALL_TRANSACTION

还发现在 base/services/java/com/android/server/wm/WindowManagerService.java 中有发送 IBinder::FIRSTCALL_TRANSACTION 消息,并注释说是 BOOT_FINISHED :

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

获取 SurfaceFlinger 服务,发送了 IBinder.FIRSTCALL_TRANSACTION 消息,看来前面的猜测是正确的。

再看看哪里调用了 performEnableScreen 函数:

xushaoqiu@ubuntu:~/svn/HS_V500/system/android4.4/frameworks$  grep -nr "performEnableScreen" *
base/services/java/com/android/server/wm/WindowManagerService.java:5239:        performEnableScreen();
base/services/java/com/android/server/wm/WindowManagerService.java:5268:        performEnableScreen();
base/services/java/com/android/server/wm/WindowManagerService.java:5271:    public void performEnableScreen() {
base/services/java/com/android/server/wm/WindowManagerService.java:5276:                Slog.i(TAG, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
base/services/java/com/android/server/wm/WindowManagerService.java:5420:            performEnableScreen();
base/services/java/com/android/server/wm/WindowManagerService.java:7383:                    performEnableScreen();
xushaoqiu@ubuntu:~/svn/HS_V500/system/android4.4/frameworks$

发现只有在WindowManagerService有引用,有四处引用:

  • enableScreenAfterBoot
  • performBootTimeout
  • showBootMessage
  • ENABLE_SCREEN 消息处理

我们可以一条一条线索顺藤摸瓜,这里只分析enableScreenAfterBoot 这条线索,因为这是正常开机走的流程:

b290366d4a2b4f82a8b5580e7a7abb5c_blob.png

只有 ActivityManagerService 直接调用了WindowManagerService.enableScreenAfterBoot() :

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

    synchronized (this) {
        updateEventDispatchingLocked();
    }   
}

而 ActivityStackSupervisor 又调用了 ActivityManagerService.enableScreenAfterBoot() :

final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
        Configuration config) {
    ...
    if (enableScreen) {
        mService.enableScreenAfterBoot();
    }   
    ...
}

其中的 mService 就是 ActivityManagerService。继续摸索又发现 ActivityManagerService 调用了 ActivityStackSupervisor.activityIdleInternalLocked():

@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.activityIdle() ,发现 ActivityThread 中有调用:

private class Idler implements MessageQueue.IdleHandler {
    @Override
        public final boolean queueIdle() {
            ...
                    if (a.activity != null && !a.activity.mFinished) {
                        try {
                            am.activityIdle(a.token, a.createdConfig, stopProfiling);
                            a.createdConfig = null;
                        } catch (RemoteException ex) {
                            // Ignore
                        }
                    }
           ...
}

ActivityThread 类就是android应用程序的主线程。这个 Idler 类是在 handleResumeActivity 被注册到主线程 ActivityThread 中的:

final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
        boolean reallyResume) {
...
        if (!r.onlyLocalRequest) {
            r.nextIdle = mNewActivities;
            mNewActivities = r;
            if (localLOGV) Slog.v(
                    TAG, "Scheduling idle handler for " + r); 
            Looper.myQueue().addIdleHandler(new Idler());
        }   
        r.onlyLocalRequest = false;

        // Tell the activity manager we have resumed.
        if (reallyResume) {
            try {
                ActivityManagerNative.getDefault().activityResumed(token);
            } catch (RemoteException ex) {
            }   
        }   
...
}

也就是一个Activity 在Resume的时候会注册一个空闲处理函数到主线程,主线程空闲的时候就会调用这个函数。

跟踪到这里就会觉得很奇怪了,为什么结束开机动画会跟Activity 的启动相关!仔细想一下也可以理解,因为android系统启动完成之后,就会启动Luancher 中的主Activity,一旦Luancher 的主Activity 启动完成之后,没有用户操作,就会进入空闲状态,ActivityThread 就会调用注册的 IdleHandler 。然后经过层层转发调用,最终调用到 SurfaceFlinger 去终止开机动画。

至此结束关机动画的流程也被我们一步一步顺藤摸瓜,整理出来啦。不过我们上面是自底向上分析,可能不大好理解,下面再自顶向下理一遍:

  1. Luancher 启动,注册了一个 Idler 空闲处理器到 ActivityThread
  2. Luancher 主线程空闲的时候会调用 Idler.queueIdle() 方法
  3. Idler.queueIdle() 通过Binder通信调用了ActivityManagerService.activityIdle()方法
  4. ActivityStackSupervisor.activityIdleInternalLocked()方法
  5. ActivityManagerService.enableScreenAfterBoot()方法
  6. WindowManagerService.enableScreenAfterBoot()方法
  7. WindowManagerService.performEnableScreen()方法
  8. performEnableScreen 通过Binder通信发送 BOOT_FINISHED 消息给 BnSurfaceComposer
  9. BnSurfaceComposer 收到 BOOT_FINISHED 消息后调用SurfaceFlinger::bootFinished 函数设置动画结束标志。

接下来我要在各个方法调用点加Log,确认这条调用路径是否正确。在加Log的过程中,突然想到 WindowManagerService.performEnableScreen() 是唯一发送 BOOT_FINISHED 消息给 SurfaceFlinger 的地方,我再这里打印一下堆栈调用信息,不就可以把整个调用链给找出来了嘛,还需要一步一步去顺藤摸瓜吗?看来脑子不够灵光啊。

0     0     0     0     0    
0 回帖