安卓S开机动画流程

Posted 一只安慕嘻

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓S开机动画流程相关的知识,希望对你有一定的参考价值。

安卓S开机动画流程

开机动画是在SurfaceFlinger实例通过调用startBootAnim()启动的,BootAnim是如何启动和结束的,总体框架图如下:

1.SurfaceFlinger进程启动

# /frameworks/native/services/surfaceflinger/surfaceflinger.rc
service surfaceflinger /system/bin/surfaceflinger
    class core animation
    user system
    group graphics drmrpc readproc
    capabilities SYS_NICE
    onrestart restart zygote
    task_profiles HighPerformance
    socket pdx/system/vr/display/client     stream 0666 system graphics u:object_r:pdx_display_client_endpoint_socket:s0
    socket pdx/system/vr/display/manager    stream 0666 system graphics u:object_r:pdx_display_manager_endpoint_socket:s0
    socket pdx/system/vr/display/vsync      stream 0666 system graphics u:object_r:pdx_display_vsync_endpoint_socket:s0

init进程会根据surfaceflinger.rc配置启动surfaceflinger进程,surfaceflinger进程(/system/bin/surfaceflinger)启动,会走到main函数里面。

# /frameworks/native/services/surfaceflinger/android.bp
filegroup 
    name: "surfaceflinger_binary_sources",
    srcs: [
        ":libsurfaceflinger_sources",
        "main_surfaceflinger.cpp",
    ],


cc_binary 
    name: "surfaceflinger",
    defaults: ["libsurfaceflinger_binary"],
    init_rc: ["surfaceflinger.rc"],
    srcs: [
        ":surfaceflinger_binary_sources",
        // Note: SurfaceFlingerFactory is not in the filegroup so that it
        // can be easily replaced.
        "SurfaceFlingerFactory.cpp",
    ],
    shared_libs: [
        "libSurfaceFlingerProp",
    ],

     logtags: ["EventLog/EventLogTags.logtags"],

可以看到编译surfaceflinger二进制进程的源文件为surfaceflinger_binary_sourcesSurfaceFlingerFactory.cpp,其中surfaceflinger_binary_sources来源于main_surfaceflinger.cpp

2.注册启动surfaceflinger服务

// /frameworks/native/services/surfaceflinger/main_surfaceflinger.cpp
int main(int, char**) 
	// ...

    sp<SurfaceFlinger> flinger = surfaceflinger::createSurfaceFlinger(); //创建surfaceflinger服务实例

	// ...
    flinger->init();  // 初始化flinger实例

    // 向ServiceManager注册surfaceflinger服务
    sp<IServiceManager> sm(defaultServiceManager());
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);

	// ...

    flinger->run();   // 启动surfaceflinger服务

    return 0;

调用SurfaceFlinger对象的init方法

// /frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::init() 
    // ...
    mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);

    if (mStartPropertySetThread->Start() != NO_ERROR) 
        ALOGE("Run StartPropertySetThread failed!");
    

    ALOGV("Done initializing");

SurfaceFlinger调用init方法时会获取mStartPropertySetThread,调用该对象的Start方法,其实是准备启动一个线程去启动BootAnimation

// /frameworks/native/services/surfaceflinger/StartPropertySetThread.cpp
status_t StartPropertySetThread::Start() 
	return run("SurfaceFlinger::StartPropertySetThread", PRIORITY_NORMAL);


bool StartPropertySetThread::threadLoop() 
    // Set property service.sf.present_timestamp, consumer need check its readiness
    property_set(kTimestampProperty, mTimestampPropertyValue ? "1" : "0");
    // 清除BootAnimation退出标志位service.bootanim.exit
    property_set("service.bootanim.exit", "0");
    // 设置bootanim的进度为0
    property_set("service.bootanim.progress", "0");
    // 通过service.bootanim.exit
    property_set("ctl.start", "bootanim");
    // 立即退出
    return false;

  • 这里一开始看起来比较疑惑,首先是StartPropertySetThread::Start函数,在StartPropertySetThread.h表明StartPropertySetThread继承自父类Thread,而父类Thread是由<utils/Thread.h> 引入的,所以这里是子类引用父类方法,这里的run函数就是就是thread的run方法。这里会启动一个线程去运行,线程名为"StartPropertySetThread",线程优先级为PRIORITY_NORMAL。线程启动以后,最终会调用 _threadLoop 函数,它会去调用threadLoop函数。这里整个函数调用栈就清楚了:

  • 当系统属性发生改变时,init进程就会接收到一个系统属性变化通知,这个通知最终是由在init进程中的函数handle_property_set_fd来处理

3.bootanim进程启动

// /frameworks/base/cmds/bootanimation/bootanimation_main.cpp
int main()

    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);

    bool noBootAnimation = bootAnimationDisabled();
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) 
        // 启动Binder线程池
        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();
        
        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());

        waitForSurfaceFlinger();
        
        boot->run("BootAnimation", PRIORITY_DISPLAY);
        // ...
    
    return 0;


bool bootAnimationDisabled() 
    char value[PROPERTY_VALUE_MAX];
    // 如果debug.sf.nobootanimation=1,则不会显示动画
    property_get("debug.sf.nobootanimation", value, "0");
    if (atoi(value) > 0) 
        return true;
    
	// 如果ro.boot.quiescent=1,则不显示开机动画
    property_get("ro.boot.quiescent", value, "0");
    if (atoi(value) > 0) 
        // Only show the bootanimation for quiescent boots if this system property is set to enabled
        if (!property_get_bool("ro.bootanim.quiescent.enabled", false)) 
            return true;
        
    

    return false;

判断完BootAnimation是不是disabled之后,如果noBootAnimation为false,则创建一个BootAnimation对象。创建完了BootAnimation对象后,调用其run方法,由于BootAnimation也继承了Thread,所以最终也会走到对应的threadLoop方法

bool BootAnimation::threadLoop()

    bool r;

    if (mZipFileName == NULL) 
        ...
     else 
        r = movie();  // 调用movie方法
    
    // 销毁 opengl 和 egl
    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::movie()

    String8 desString;
    // 读取 desc.txt 配置文件
    if (!readFile("desc.txt", desString)) 
        return false;
    
    char const* s = desString.string();

    // 解析描述文件
    for (;;) 
        ...
    

    for (size_t i=0 ; i<pcount ; i++) 
        for (int r=0 ; !part.count || r<part.count ; r++) 
            // opengl 绘制操作
            glClearColor(
                    part.backgroundColor[0],
                    part.backgroundColor[1],
                    part.backgroundColor[2],
                    1.0f);
            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 
                    ...
                    initTexture(frame);
                
                
                // specify the y center as ceiling((mHeight - animation.height) / 2)
                // which is equivalent to mHeight - (yc + animation.height)
                glDrawTexiOES(xc, mHeight - (yc + animation.height),
                              0, animation.width, animation.height);
                eglSwapBuffers(mDisplay, mSurface);

                // 不断绘制时检测是否需要退出
                checkExit();
            
            // 如果退出了就跳出结束绘制
            if(exitPending() && !part.count)
                break;
        

        // free the textures for this part
        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;


// 读取 service.bootanim.exit 值是否是 1 
#define EXIT_PROP_NAME "service.bootanim.exit"
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();
        
    

启动动画底层采用的是 opengles 的方式来渲染绘制的,绘制的内容是本地的一个启动动画资源包,在绘制的过程中会不断的判断是否需要退出,读取的字段是 service.bootanim.exit ,为 1 代表需要 break 退出循环绘制。因此我们只需要找到 service.bootanim.exit 在哪里设置为 1 的,便可找到退出启动动画的入口。关闭动画的入口还是在 SurfaceFlinger 中只是这个调用流程比较复杂而已:

final void handleResumeActivity(IBinder token,
                                boolean clearHide, boolean isForward, boolean reallyResume) 
    ActivityClientRecord r = performResumeActivity(token, clearHide);
    if (r != null) 
        if (!r.onlyLocalRequest) 
            r.nextIdle = mNewActivities;
            mNewActivities = r;
            // 添加了一个 IdleHandler 消息
            Looper.myQueue().addIdleHandler(new Idler());
        
     else 
        ...
    


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 
                        // 调用 AMS 的 activityIdle
                        am.activityIdle(a.token, a.createdConfig, stopProfiling);
                     catch (RemoteException ex) 
                        // Ignore
                    
                
             while (a != null);
        
        return false;
    



@Override
    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) 
    synchronized (this) 
        ActivityStack stack = ActivityRecord.getStackLocked(token);
        if (stack != null) 
            ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token, false, config);
        
    
    Binder.restoreCallingIdentity(origId);


// Checked.
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
                                                Configuration config) 
    ActivityRecord r = ActivityRecord.forTokenLocked(token);
    if (r != null) 
        ...
            if (isFrontStack(r.task.stack) || fromTimeout) 
                booting = checkFinishBootingLocked();
            
    
    ...
        return r;


private boolean checkFinishBootingLocked() 
    final boolean booting = mService.mBooting;
    boolean enableScreen = false;
    mService.mBooting = false;
    if (!mService.mBooted) 
        mService.mBooted = true;
        enableScreen = true;
    
    if (booting || enableScreen) 
        mService.postFinishBooting(booting, enableScreen);
    
    return booting;


void enableScreenAfterBoot() 
    mWindowManager.enableScreenAfterBoot();
    synchronized (this) 
        updateEventDispatchingLocked();
    


public void performEnableScreen() 
    synchronized(mWindowMap) 
        if (!mBootAnimationStopped) 
            // 向SurfaceFlinger 进程发起关闭开机界面的消息
            try 
                IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                if (surfaceFlinger != null) 
                    Parcel data = Parcel.obtain();
                    data.writeInterfaceToken("android.ui.ISurfaceComposer");
                    // 向SurfaceComposer发送
                    surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
                                            data, null, 0);
                    data.recycle();
                
             catch (RemoteException ex) 
                ...
            
            mBootAnimationStopped = true;
        
        ...
    


// ----------------------------------------------------------------------
enum ISurfaceComposerTag 
    BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
    // ...


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

// ----------------------------------------------------------------------

void SurfaceFlinger::bootFinished() 
    // ...
    // 把 service.bootanim.exit 属性设置为 1 ,bootanim 进程读到 1 时就会退出开机启动动画
    property_set("service.bootanim.exit", "1");

关闭开机启动动画的流程还是比较复杂的,我们来缕一缕整个逻辑,我们的 Launcher 进程启动后会启动我们 Launcher Activity 界面,而 Activity 的生命周期调用都是由 ActivityThread 来执行的,其中就会执行到 handleResumeActivity 方法,在该方法中会添加一个 IdleHandler 消息,会调用到 AMS 的 activityIdle 方法,AMS 会调用 WMS 的 enableScreenAfterBoot 方法,WMS 会跨进程通知 SurfaceFlinger 去关闭我们的开机启动动画。

4.开机动画包里有什么

这里建议先看一下官方文档:/frameworks/base/cmds/bootanimation/FORMAT.md

开机动画指的是以bootanimation.zip方式存在,启动的时候会依次选择一个bootanimation.zip加载

  1. /system/media/bootanimation-encrypted.zip (if getprop(“vold.decrypt”) = ‘1’)
  2. /system/media/bootanimation.zip
  3. /oem/media/bootanimation.zip

bootanimation.zip 文件中包含:

desc.txt - a text file
part0  \\
part1   \\  directories full of PNG frames
...     /
partN  /

“desc.txt”用来描述用户自定义的开机动画是如何显示的

以下面的例子为例:

1280 720 1

p 1 1 part0

p 0 1 part1

第一行的三个数字分别表示开机动画在屏幕中的显示宽度、高度以及帧速(fps)。剩余的每一行都用来描述一个动画片断,这些行必须要以字符“p”来开头,后面紧跟着两个数字以及一个文件目录路径名称。
第一个数字表示一个片断的循环显示次数,如果它的值等于0,那么就表示无限循环地显示该动画片断。
第二个数字表示每一个片断在两次循环显示之间的时间间隔。这个时间间隔是以一个帧的时间为单位的。
文件目录下面保存的是一系列png文件,这些png文件会被依次显示在屏幕中。

参考资料:

  • https://blog.51cto.com/u_11176305/3796348
  • https://www.cnblogs.com/lufeibin/p/13529981.html
  • https://blog.csdn.net/weixin_36044720/article/details/117277602?spm=1001.2014.3001.5506

以上是关于安卓S开机动画流程的主要内容,如果未能解决你的问题,请参考以下文章

小米手机安卓11可以更换安卓10的开机动画吗

请问安卓4.2.2如何修改开机LOGO图片,不是开机动画!!

如何把安卓开机动画,换成谷歌新logo

安卓手机怎么修改自定义开机动画

安卓手机开机画面修改

如何修改android开机画面,开机动画