Android 开机动画的启动

简介: Android 开机动画的启动

开机动画位置:device\xxx\common\logo\bootanimation\bootanimation.zip

desc.txt用于描述动画如何显示

文件格式如下:

WIDTH HEIGHT FPS

//WIDTH 图片宽度(px)

//HEIGHT 图片高度(px)

//FPS:每秒帧数

 

TYPE COUNT PAUSE PATH

//TYPE:动画类型,p:如果系统启动完毕,会中断播放。默认;

                                c:一直播放完毕,无论系统有没启动完毕

//COUNT:播放次数,0-无限循环

//PAUSE:本组播放完毕后,停留的帧数。

第一行的三个数字分别表示开机动画在屏幕中的显示宽度、高度以及帧速(fps)。

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

动画的Start和stop控制:

动画的开始与结束是由属性控制的,由/system/bin/surfaceflinger来控制,然后相关的动画处理程序为/system/bin/bootanimation,在init.rc中指定。

bootanimation 需要 由property_set(“ctl.start”, “bootanim”);来启动进程,

                               由property_set(“ctl.stop”, “bootanim”);来关掉进程。

”service.bootanim.exit”:这个属性在bootanimation进程里会周期检查,=1时就退出动画,=0表示要播放动画。

主要过程:SurfaceFlinger 服务启动的过程中会修改系统属性"ctl.start"的值,以通知init进程启动bootanim来显示开机动画。当系统关键服务启动完毕后,由AMS通知SurfaceFlinger修改系统属性"ctl.stop"来通知init进程停止执行bootanim关闭动画。

bootanimation

开机动画是由应用程序bootanimation来负责显示的,先看一下其rc文件。

frameworks\base\cmds\bootanimation\bootanim.rc

service bootanim /system/bin/bootanimation
    class core animation
    user graphics
    group graphics audio
    disabled //系统启动时,不会自动启动bootanimation
    oneshot  //只启动一次
    ioprio rt 0
    task_profiles MaxPerformance

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

surfaceflinger的启动时机

在高版本的Android上,如AndroidP,surfaceflinger进程并不是直接在init.rc文件中启动的,而是通过Android.bp文件去包含启动surfaceflinger.rc文件,然后在该文件中再去启动surfaceflinger:

frameworks\native\services\surfaceflinger\Android.bp

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启动了,就会跑到它的main函数:

SurfaceFlinger服务的入口在main_surfaceflinger.cpp中

frameworks\native\services\surfaceflinger\main_surfaceflinger.cpp

int main(int, char**) {
    signal(SIGPIPE, SIG_IGN);
 
    hardware::configureRpcThreadpool(1 /* maxThreads */,
            false /* callerWillJoin */);
 
    startGraphicsAllocatorService();
 
    // 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 = surfaceflinger::createSurfaceFlinger();
 
    setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
 
    set_sched_policy(0, SP_FOREGROUND);
 
    // Put most SurfaceFlinger threads in the system-background cpuset
    // Keeps us from unnecessarily using big cores
    // Do this after the binder thread pool init
    if (cpusets_enabled()) set_cpuset_policy(0, SP_SYSTEM);
 
    // initialize before clients can connect
    flinger->init();
 
    // publish surface flinger
    sp<IServiceManager> sm(defaultServiceManager());
    sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false,
                   IServiceManager::DUMP_FLAG_PRIORITY_CRITICAL | IServiceManager::DUMP_FLAG_PROTO);
 
    startDisplayService(); // dependency on SF getting registered above
 
    if (SurfaceFlinger::setSchedFifo(true) != NO_ERROR) {
        ALOGW("Couldn't set to SCHED_FIFO: %s", strerror(errno));
    }
 
    // run surface flinger in this thread
    flinger->run();
 
    return 0;
}

frameworks\native\services\surfaceflinger\SurfaceFlinger.cpp

init方法中 start mStartPropertySetThread

    const bool presentFenceReliable =
            !getHwComposer().hasCapability(hal::Capability::PRESENT_FENCE_IS_NOT_RELIABLE);
    mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);
 
    if (mStartPropertySetThread->Start() != NO_ERROR) {
        ALOGE("Run StartPropertySetThread failed!");
    }
 
    ALOGV("Done initializing");

frameworks\native\services\surfaceflinger\SurfaceFlingerDefaultFactory.cpp

sp<StartPropertySetThread> DefaultFactory::createStartPropertySetThread(
        bool timestampPropertyValue) {
    return new StartPropertySetThread(timestampPropertyValue);
}

frameworks\native\services\surfaceflinger\StartPropertySetThread.cpp

#include <cutils/properties.h>
#include "StartPropertySetThread.h"
 
namespace android {
 
StartPropertySetThread::StartPropertySetThread(bool timestampPropertyValue):
        Thread(false), mTimestampPropertyValue(timestampPropertyValue) {}
 
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");
    // Clear BootAnimation exit flag
    property_set("service.bootanim.exit", "0");
    // Start BootAnimation if not started
    property_set("ctl.start", "bootanim");
    // Exit immediately
    return false;
}
 
} /

这里设置属性【service.bootanim.exit】并采用【ctl.start】的方式启动开机动画:

在这之后,开机动画就会启动,由bootanimation进程实现具体动画播放

bootAnimation的启动

名称等于"bootanim"的服务所对应的应用程序为/system/bin/bootanimation,应用程序入口函数的实现在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) {
 
        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();
 
        // create the boot animation object (may take up to 200ms for 2MB zip)
        sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks());
 
        waitForSurfaceFlinger();
 
        boot->run("BootAnimation", PRIORITY_DISPLAY);
 
        ALOGV("Boot animation set up. Joining pool.");
 
        IPCThreadState::self()->joinThreadPool();
    }
    return 0;
}

首先检查系统属性“debug.sf.nobootnimaition”的值是否等于0。如果不等于的话,那么接下来就会启动一个Binder线程池,并且创建一个BootAnimation对象。这个Binder线程用于同SurfaceFlinger服务通信。

frameworks\base\cmds\bootanimation\BootAnimation.cpp

BootAnimation类间接地继承了RefBase类,并且重写了RefBase类的成员函数onFirstRef,因此,当一个BootAnimation对象第一次被智能指针引用的时,这个BootAnimation对象的成员函数onFirstRef就会被调用。其中几个重要的函数说明如下:

onFirstRef()—— 属于其父类RefBase,该函数在强引用sp新增引用计数時调用,就是当有sp包装的类初始化的时候调用;

binderDied() ——当对象死掉或者其他情况导致该Binder结束时,就会回调binderDied()方法;

readyToRun() ——Thread执行前的初始化工作;

threadLoop() ——每个线程类都要实现的,在这里定义thread的执行内容。这个函数如果返回true,且没有调用requestExit(),则该函数会再次执行;如果返回false,则threadloop中的内容仅仅执行一次,线程就会退出。

其他函数简述如下:

android()——显示系统默认的开机画面;

movie()——显示用户自定义的开机动画;

loadAnimation(const String8&)——加载动画;

playAnimation(const Animation&)——播放动画;

checkExit()——检查是否退出动画;

void BootAnimation::onFirstRef() {
    status_t err = mSession->linkToComposerDeath(this);
    SLOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
    if (err == NO_ERROR) {
        // Load the animation content -- this can be slow (eg 200ms)
        // called before waitForSurfaceFlinger() in main() to avoid wait
        ALOGD("%sAnimationPreloadTiming start time: %" PRId64 "ms",
                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
        preloadAnimation();
        ALOGD("%sAnimationPreloadStopTiming start time: %" PRId64 "ms",
                mShuttingDown ? "Shutdown" : "Boot", elapsedRealtime());
    }
}

mSession是BootAnimation类的一个成员变量,它的类型为SurfaceComposerClient,是用来和SurfaceFlinger执行Binder进程间通信的,它是在BootAnimation类的构造函数中创建的

mSession = new SurfaceComposerClient();

由于BootAnimation类引用了SurfaceFlinger服务,因此,当SurfaceFlinger服务意外死亡时,BootAnimation类就需要得到通知,这是通过调用成员变量mSession的成员函数linkToComposerDeath来注册SurfaceFlinger服务的死亡接收通知来实现的。

 

       BootAnimation类继承了Thread类,因此,当bootanimation_main.cpp调用了Thread的成员函数run之后,系统就会创建一个线程,这个线程在第一次运行之前,会调用BootAnimation类的成员函数readyToRun来执行一些初始化工作,后面再调用BootAnimation类的成员函数threadLoop来显示第三个开机画面。

 

bool BootAnimation::threadLoop() {
    bool result;
    // We have no bootanimation file, so we use the stock android logo
    // animation.
    if (mZipFileName.isEmpty()) {
        result = android();
    } else {
        result = movie();
    }
 
    mCallbacks->shutdown();
    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(mDisplay, mContext);
    eglDestroySurface(mDisplay, mSurface);
    mFlingerSurface.clear();
    mFlingerSurfaceControl.clear();
    eglTerminate(mDisplay);
    eglReleaseThread();
    IPCThreadState::self()->stopProcess();
    return result;
}

如果mZipFileName不为空,那么接下来就会调用BootAnimation类的成员函数android来显示系统默认的开机动画,否则的话,就会调用BootAnimation类的成员函数movie来显示用户自定义的开机动画

bool BootAnimation::findBootAnimationFileInternal(const std::vector<std::string> &files) {
    for (const std::string& f : files) {
        if (access(f.c_str(), R_OK) == 0) {
            mZipFileName = f.c_str();
            return true;
        }
    }
    return false;
}

bootanim的关闭

init启动zygote进程之后,由zygote孵化出了system_server,然后system_server启动了各种各种的系统所需的服务,其中就有AMS,AMS启动并ready后,会执行startHomeActivityLocked:

void SurfaceFlinger::bootFinished()
{
    if (mBootFinished == true) {
        ALOGE("Extra call to bootFinished");
        return;
    }
    mBootFinished = true;
    if (mStartPropertySetThread->join() != NO_ERROR) {
        ALOGE("Join StartPropertySetThread failed!");
    }
    const nsecs_t now = systemTime();
    const nsecs_t duration = now - mBootTime;
    ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
 
    mFrameTracer->initialize();
    mTimeStats->onBootFinished();
 
    // wait patiently for the window manager death
    const String16 name("window");
    mWindowManager = defaultServiceManager()->getService(name);
    if (mWindowManager != 0) {
        mWindowManager->linkToDeath(static_cast<IBinder::DeathRecipient*>(this));
    }
 
    if (mVrFlinger) {
      mVrFlinger->OnBootFinished();
    }
 
    // 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");
 
    const int LOGTAG_SF_STOP_BOOTANIM = 60110;
    LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                   ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
 
    sp<IBinder> input(defaultServiceManager()->getService(String16("inputflinger")));
 
    static_cast<void>(schedule([=] {
        if (input == nullptr) {
            ALOGE("Failed to link to input service");
        } else {
            mInputFlinger = interface_cast<IInputFlinger>(input);
        }
 
        readPersistentProperties();
        mPowerAdvisor.onBootFinished();
        mBootStage = BootStage::FINISHED;
 
        if (property_get_bool("sf.debug.show_refresh_rate_overlay", false)) {
            enableRefreshRateOverlay(true);
        }
    }));
}

AMS在systemReady后会启动launcher

  if (bootingSystemUser) {
                t.traceBegin("startHomeOnAllDisplays");
                mAtmInternal.startHomeOnAllDisplays(currentUserId, "systemReady");
                t.traceEnd();
            }

frameworks\base\services\core\java\com\android\server\wm\ActivityTaskManagerInternal.java

public abstract boolean startHomeOnAllDisplays(int userId, String reason);

frameworks\base\services\core\java\com\android\server\wm\ActivityTaskManagerService.java

final class LocalService extends ActivityTaskManagerInternal {
    @Override
        public boolean startHomeOnAllDisplays(int userId, String reason) {
            synchronized (mGlobalLock) {
                return mRootWindowContainer.startHomeOnAllDisplays(userId, reason);
            }
        }

frameworks\base\services\core\java\com\android\server\wm\RootWindowContainer.java

    boolean startHomeOnAllDisplays(int userId, String reason) {
        boolean homeStarted = false;
        for (int i = getChildCount() - 1; i >= 0; i--) {
            final int displayId = getChildAt(i).mDisplayId;
            homeStarted |= startHomeOnDisplay(userId, reason, displayId);
        }
        return homeStarted;
    }
 
    void startHomeOnEmptyDisplays(String reason) {
        for (int i = getChildCount() - 1; i >= 0; i--) {
            final DisplayContent display = getChildAt(i);
            for (int tdaNdx = display.getTaskDisplayAreaCount() - 1; tdaNdx >= 0; --tdaNdx) {
                final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tdaNdx);
                if (taskDisplayArea.topRunningActivity() == null) {
                    startHomeOnTaskDisplayArea(mCurrentUser, reason, taskDisplayArea,
                            false /* allowInstrumenting */, false /* fromHomeKey */);
                }
            }
        }
    }
 
    boolean startHomeOnDisplay(int userId, String reason, int displayId) {
        return startHomeOnDisplay(userId, reason, displayId, false /* allowInstrumenting */,
                false /* fromHomeKey */);
    }
 
    boolean startHomeOnDisplay(int userId, String reason, int displayId, boolean allowInstrumenting,
            boolean fromHomeKey) {
        // Fallback to top focused display or default display if the displayId is invalid.
        if (displayId == INVALID_DISPLAY) {
            final ActivityStack stack = getTopDisplayFocusedStack();
            displayId = stack != null ? stack.getDisplayId() : DEFAULT_DISPLAY;
        }
 
        final DisplayContent display = getDisplayContent(displayId);
        boolean result = false;
        for (int tcNdx = display.getTaskDisplayAreaCount() - 1; tcNdx >= 0; --tcNdx) {
            final TaskDisplayArea taskDisplayArea = display.getTaskDisplayAreaAt(tcNdx);
            result |= startHomeOnTaskDisplayArea(userId, reason, taskDisplayArea,
                    allowInstrumenting, fromHomeKey);
        }
        return result;
    }

frameworks\base\services\core\java\com\android\server\wm\ActivityTaskManagerService.java

    void postFinishBooting(boolean finishBooting, boolean enableScreen) {
        mH.post(() -> {
            if (finishBooting) {
                mAmInternal.finishBooting();
            }
            if (enableScreen) {
                mInternal.enableScreenAfterBoot(isBooted());
            }
        });
    }
        @Override
        public void enableScreenAfterBoot(boolean booted) {
            synchronized (mGlobalLock) {
                writeBootProgressEnableScreen(SystemClock.uptimeMillis());
                mWindowManager.enableScreenAfterBoot();
                updateEventDispatchingLocked(booted);
            }
        }

frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java

    public void enableScreenAfterBoot() {
        synchronized (mGlobalLock) {
            ProtoLog.i(WM_DEBUG_BOOT, "enableScreenAfterBoot: mDisplayEnabled=%b "
                            + "mForceDisplayEnabled=%b mShowingBootMessages=%b mSystemBooted=%b. "
                            + "%s",
                    mDisplayEnabled, mForceDisplayEnabled, mShowingBootMessages, mSystemBooted,
                    new RuntimeException("here").fillInStackTrace());
            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();
    }

enableScreenAfterBoot()经过多次调用就会执行WMS的performEnableScreen()方法,在此方法中我们就可以看到surfaceflinger的身影了,通过transact调用发送BOOT_FINISHED的消息给surfaceflinger。

    frameworks\base\services\core\java\com\android\server\wm\WindowManagerService.java
    private void performEnableScreen() {
        ...
 
            try {
                IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
                if (surfaceFlinger != null) {
                    Slog.i(TAG_WM, "******* 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_WM, "Boot completed: SurfaceFlinger is dead!");
            }
        ...
    }

frameworks\native\libs\gui\include\gui\ISurfaceComposer.h

class BnSurfaceComposer: public BnInterface<ISurfaceComposer> {
public:
    enum ISurfaceComposerTag {
        // Note: BOOT_FINISHED must remain this value, it is called from
        // Java by ActivityManagerService.
        BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,

WMS最终通过binder调用,经过ISurfaceComposer处理,最终通知SurfaceFlinger关闭开机动画。

frameworks\native\services\surfaceflinger\SurfaceFlinger.cpp
void SurfaceFlinger::bootFinished()
{
    if (mStartPropertySetThread->join() != NO_ERROR) {
        ALOGE("Join StartPropertySetThread failed!");
    }
    const nsecs_t now = systemTime();
    const nsecs_t duration = now - mBootTime;
    ALOGI("Boot is finished (%ld ms)", long(ns2ms(duration)) );
 
    // 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));
    }
 
    if (mVrFlinger) {
      mVrFlinger->OnBootFinished();
    }
 
    // 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");
 
    const int LOGTAG_SF_STOP_BOOTANIM = 60110;
    LOG_EVENT_LONG(LOGTAG_SF_STOP_BOOTANIM,
                   ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
 
    sp<LambdaMessage> readProperties = new LambdaMessage([&]() {
        readPersistentProperties();
    });
    postMessageAsync(readProperties);
}

至此开机动画结束。

frameworks\base\cmds\bootanimation\BootAnimation.cpp

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

requestExit(); kill掉bootanime进程

system\core\libutils\Threads.cpp

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

至此bootanime进程死亡。


目录
相关文章
|
30天前
|
Java API 调度
Android系统 自定义开机广播,禁止后台服务,运行手动安装应用接收开机广播
Android系统 自定义开机广播,禁止后台服务,运行手动安装应用接收开机广播
59 0
|
30天前
|
安全 Shell Android开发
Android系统 init.rc开机执行shell脚本
Android系统 init.rc开机执行shell脚本
120 0
|
30天前
|
Java Android开发 开发者
Android10 修改开发者选项中动画缩放默认值
Android10 修改开发者选项中动画缩放默认值
20 0
|
30天前
|
XML Java Android开发
android的三种动画
android的三种动画
17 0
|
27天前
|
数据库 Android开发
Android数据库框架-GreenDao入门,2024年最新flutter 页面跳转动画
Android数据库框架-GreenDao入门,2024年最新flutter 页面跳转动画
Android数据库框架-GreenDao入门,2024年最新flutter 页面跳转动画
|
30天前
|
Linux Android开发
Android开机不显示bootloader界面
Android开机不显示bootloader界面
19 0
Android开机不显示bootloader界面
|
30天前
|
Java Android开发
Android Mediatek 延迟停止启动动画和通知SurfaceFlinger(Android正在启动)
Android Mediatek 延迟停止启动动画和通知SurfaceFlinger(Android正在启动)
28 0
|
30天前
|
Java Android开发
Android开发之使用OpenGL实现翻书动画
本文讲述了如何使用OpenGL实现更平滑、逼真的电子书翻页动画,以解决传统贝塞尔曲线方法存在的卡顿和阴影问题。作者分享了一个改造后的外国代码示例,提供了从前往后和从后往前的翻页效果动图。文章附带了`GlTurnActivity`的Java代码片段,展示如何加载和显示书籍图片。完整工程代码可在作者的GitHub找到:https://github.com/aqi00/note/tree/master/ExmOpenGL。
44 1
Android开发之使用OpenGL实现翻书动画
|
30天前
|
XML 开发工具 Android开发
Android动画效果-更新中
Android动画效果-更新中
65 1
|
30天前
|
XML Android开发 数据格式
[Android]动画
[Android]动画
43 0