Android 开机动画流程分析

Android bootanim流程分析

bootanim启动步骤

  1. init.rc 文件声明两个重要的服务 bootanim, surfaceflinger
  2. 启动界面依赖于 surface 管理服务,优先启动显示管理服务 surfaceflinger
  3. surfaceflinger 服务完成后,通过 (“ctl.start”, “bootanim”) 启动 bootanim 开机动画
  4. 当 system_server 启动系统 HomeUI 的时候关闭 bootanim

    其具体流程可以参考下图所示

  • init.rc 文件相关声明

    surfaceflinger类属core,随core类服务一同启动,服务重启直接导致Zygote服务的重启

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

    bootanim类属core,但是disable标志它不随core服务一同启动,用户及用户组均是graphics,而且仅启动一次oneshot

    service bootanim /system/bin/bootanimation
        class core
        user graphics
        group graphics audio
        disabled
        oneshot
    

surfaceflinger服务的启动流程

  • surfaceflinger 的 main 方法

    –> frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp

    int main(int, char**) {
        // 设置线程数量最大4
        ProcessState::self()->setThreadPoolMaxThreadCount(4);
    
        // 开启线程池
        sp<ProcessState> ps(ProcessState::self());
        ps->startThreadPool();
    
        // 实例surfaceflinger
        sp<SurfaceFlinger> flinger = new SurfaceFlinger();
    
        setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
    
        set_sched_policy(0, SP_FOREGROUND);
    
        // 在客户端连接前初始化
        flinger->init();
    
        // 发布surfaceflinger就绪
        sp<IServiceManager> sm(defaultServiceManager());
        sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);
    
        // 启动
        flinger->run();
    
        return 0;
    }
    
  • surfaceflinger 在 init 中启动 bootanim 服务

    –> frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

    surfaceflinger 的初始化工作代码众多,这里就不一一列举了,在其init过程中,启动了bootanim服务

    void SurfaceFlinger::init() {
    
        // 初始化 EGL 用于显示
        mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        eglInitialize(mEGLDisplay, NULL, NULL);
    
        mHwc = new HWComposer(this,
                *static_cast<HWComposer::EventHandler *>(this));
    
        ...
    
        mDrawingState = mCurrentState;
    
        initializeDisplays();
    
        // 启动 boot animation
        startBootAnim();
    
       ...
    
    }
    
  • init 中通过 property_set(“ctl.start”, “bootanim”) 启动了 bootanim 服务

    void SurfaceFlinger::startBootAnim() {
        // 开启 bootanim 服务
        struct timespec t = {0, 0};
        clock_gettime(CLOCK_MONOTONIC, &t);
    
        // 置位操作
        property_set("service.bootanim.exit", "0");
        property_set("ctl.start", "bootanim");
    }
    

bootanim 服务的启动流程

  • 总体概述

    –> frameworks/base/cmds/bootanimation/BootAnimation.cpp

    bootanim 流程如下图所示:

    bootanim内部有大量的逻辑用于初始化和surfaceflinger通信,其内部依赖于安卓独有的binder通信机制,这里不做过多的说明。关于binder通信机制,需要大量篇幅的介绍说明,个人目前还没有这个能力。下面我们来看点可以看得懂的干货

  • readyToRun 流程

    status_t BootAnimation::readyToRun() {
    
       ...
    
        // 创建 native层surface
        sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
                dinfo.w, dinfo.h, PIXEL_FORMAT_RGBA_8888, 0);
    
        SurfaceComposerClient::openGlobalTransaction();
        SurfaceComposerClient::closeGlobalTransaction();

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

        // 初始化 opengl 和 egl
        EGLint w, h;
        EGLint numConfigs;
        EGLConfig config;
        EGLSurface surface;
        EGLContext context;

        EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
        eglInitialize(display, 0, 0);
        eglChooseConfig(display, attribs, &config, 1, &numConfigs);

        ...

        // 设备支持编码选项或者正处于编码进程中,显示编码动画
        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;
        }

        // 查看是否存在需要播放的video文件
        if ((property_get("mstar.bootvideo", decrypt, NULL) > 0) ||
            (access(USER_BOOTVIDEO_FILE, F_OK) == 0)) {
            mVideo = true;
            mZip = NULL;
        } else {
            mVideo = false;
            if(property_get("mstar.close.bootlogo.gop.frame", decrypt, NULL) <= 0 ) {
                property_set("mstar.close.bootlogo.gop.frame", "3");
            }
        }
           return NO_ERROR;
    }

这里需要注意mZip文件的获取,其三个文件的文件夹路径如下:

    #define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip"
    #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip"
    #define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip"
    #define USER_BOOTVIDEO_FILE "/data/video/video.ts"

通过代码可以看出,文件的获取优先级是bootanimation-encrypted.zip最高,/oem/media/文件夹下的bootanimation.zip次之,最低级的是/system/media/文件夹下的bootanimation.zip。最后在检查指定目录是否存在用于启动播放的video视频。
  • threadLoop 流程

    bool BootAnimation::threadLoop()
    {
        bool r;
        // 等待硬件初始化完成,开启 bootanim 动画
        char property[PROPERTY_VALUE_MAX] = {0};
        property_get("mstar.hw.init", property, "0");
        int hwInit = atoi(property);
        memset(property, 0, PROPERTY_VALUE_MAX);
        property_get("mstar.str.suspending", property, "0");
        int strSuspending = atoi(property);
        if (hwInit == 0) {
            int exitnow;
    
            property_get(EXIT_PROP_NAME, property, "0");
            exitnow = atoi(property);
            if (exitnow) {
                r = false;
                goto exit;
            }
            return true;
        }
    
        // 存在video,播放它
        // 存在bootanim文件,播放它
        // 其他情况,显示静态logo
        if (mVideo && (strSuspending == 0)) {
            r = video();
        } else if (mZip == NULL) {
            r = android();
        } else {
            r = movie();
        }
    
    exit:
    
        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;
    }
    

    线程循环等待中的逻辑就更简单了。首先监测系统的硬件是否初始化完成,确保播放动画的相关配置均已ok,然后分别对上面获取的指定文件进行判断,优先播放video视频,其次是自定义的bootanim动画,如果什么都没有,就显示静态logo。

    对于显示的具体流程这里就不做分析了,后续会对如何定制 bootanim.zip 文件进行集中的分析。这里我们只看一下代码显示logo中如何检测开机完成并关闭动画的逻辑。

  • 动画显示关闭流程

    bool BootAnimation::android()
    {
        // 初始化静态logo图片
        initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png");
        initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png");

        // 清除屏幕
        glShadeModel(GL_FLAT);
        glDisable(GL_DITHER);
        glDisable(GL_SCISSOR_TEST);

           ...

        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;

            ...

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

显示静态logo中,死循环监测动画是否退出。

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

    #define EXIT_PROP_NAME "service.bootanim.exit"

checkExit中监测 value = "service.bootanim.exit" 的属性值,如果等于1,那么就要退出动画。

开机动画的关闭

  • 概述

    当SystemServer将系统中的关键服务启动完成后,会启动桌面启动器Launcher。Launcher启动后,会向ActivityManagerService发送一个Activity组件空闲通知,AMS收到该通知后,就会调用成员函数enableScreenAfterBoot()停止开机动画,以便让屏幕显示桌面。

  • AMS -> enableScreenAfterBoot

    void enableScreenAfterBoot() {
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_ENABLE_SCREEN,
                SystemClock.uptimeMillis());
    
        // 通知WMS enableScreenAfterBoot
        mWindowManager.enableScreenAfterBoot();
    
        synchronized (this) {
            updateEventDispatchingLocked();
        }
    }
    
  • WMS -> enableScreenAfterBoot

    由AMS间接调用WMS的enableScreenAfterBoot通知开机完成。
    
    public void enableScreenAfterBoot() {
        synchronized(mWindowMap) {
    
            ...
    
            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();
    }
    
  • 准备桌面 -> performEnableScreen

    public void performEnableScreen() {
        synchronized(mWindowMap) {
    
            ...
    
            if (!mBootAnimationStopped) {
                // Do this one time.
                try {
    
                    // 通过binder通信机制,向SurfaceFlinger发送消息,停止开机动画
                    IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                    if (surfaceFlinger != null) {
                        Parcel data = Parcel.obtain();
                        data.writeInterfaceToken("android.ui.ISurfaceComposer");
    
                        //消息内容为 IBinder.FIRST_CALL_TRANSACTION
                        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;
            }
    
            ...
        }
    }
    

    代码可以看出,通过binder通信机制,获取到远程的surfaceflinger服务,向其发送 IBinder.FIRST_CALL_TRANSACTION 消息,远程服务接收到此消息再做处理。

  • surfaceflinger 接收消息处理

    根据其传递的接口名”android.ui.ISurfaceComposer”,找到其文件位置,如下

    –> frameworks/native/include/gui/ISurfaceComposer.h
    –> frameworks/native/libs/gui/ISurfaceComposer.cpp

    其头文件又如下枚举定义值

    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,
                CREATE_CONNECTION,
                CREATE_GRAPHIC_BUFFER_ALLOC,
                CREATE_DISPLAY_EVENT_CONNECTION,
    
                ...
                }
    }
    

    这里可以看到BOOT_FINISHED消息的定义,之后我们可以在 ISurfaceComposer.cpp 中找到处理这个消息的逻辑

  • ISurfaceComposer.cpp 处理 BOOT_FINISHED 消息

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

    在 ISurfaceComposer.cpp 中的onTransact方法中,集中处理发来的binder消息,这里可以看出在对WMS发来的 BOOT_FINISHED(IBinder::FIRST_CALL_TRANSACTION) 消息,直接回掉bootFinished()方法。

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

    SurfaceFlinger 继承 BnSurfaceComposer,BnSurfaceComposer 又继承 BnInterface,BnInterface 时定义好的模版,形参为 ISurfaceComposer。SurfaceFlinger的bootFinished方法如下:

    void SurfaceFlinger::bootFinished(){
        const nsecs_t now = systemTime();
        const nsecs_t duration = now - mBootTime;
        mBootFinished = true;
    
        struct timespec t = {0, 0};
        clock_gettime(CLOCK_MONOTONIC, &t);
    
        const String16 name("window");
        sp<IBinder> window(defaultServiceManager()->getService(name));
        if (window != 0) {
            window->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
        }
    
        // 置位关闭动画属性值
        property_set("service.bootanim.exit", "1");
    }
    

    走到这里,serfaceflinger就完成了关闭动画的属性值更改操作,后面就是bootanim程序死循环中不断检测指定属性值实现动画的关闭。

  • bootanim 循环监测实现关闭动画

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

总结

安卓开机动画的显示和消失,涉及的内容还是挺多的,想要详细了解其内部的实现流程还是有一定难度的。尤其是在binder消息的传递和处理上面,各个部分来回跳转,真的很头疼。对于bootanim.zip的自定义实现,后续会集中根据代码的解析来分析内部文件的具体含义。

  • 引用参考博客中很好的一幅图片来收尾:

参考博客地址

坚持原创技术分享,您的支持将鼓励我继续创作!