Android開機動畫流程
Android系統在啟動過程中,最多可以出現三個畫面。第一個開機畫面是在核心啟動的過程中出現,是一個靜態的畫面;第二個畫面是在init程序啟動的過程中出現的,也是一個靜態的畫面;第三個畫面是在系統服務啟動的過程中出現 ,是一個動態的畫面。這三個畫面都是在一個被稱為幀緩衝區(frame buffer,簡稱fb)的硬體裝置上進行渲染的。本文主要分析第三個啟動畫面的流程。
在Android層動畫的流程圖:
先由init啟動SurfaceFlinger程序,對圖形系統進行初始化,再由SurfaceFlinger程序啟動BootAnimation 程序顯示動畫。動畫由一個 while 迴圈播放,在播放迴圈裡呼叫 checkExit ()檢查一個系統標誌,判斷是否應該結束動畫。由此可見,動畫的顯示流程較為簡單,但動畫的結束流程相對複雜,其中經歷了Launcher、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:
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <sys/resource.h>
#include <cutils/sched_policy.h>
#include <binder/IServiceManager.h>
#include <binder/IPCThreadState.h>
#include <binder/ProcessState.h>
#include <binder/IServiceManager.h>
#include "GpuService.h"
#include "SurfaceFlinger.h"
using namespace android;
int main(int, char**) {
signal(SIGPIPE, SIG_IGN);
// 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
//建立了一個SurfaceFlinger物件flinger
sp<SurfaceFlinger> flinger = new SurfaceFlinger();
setpriority(PRIO_PROCESS, 0, PRIORITY_URGENT_DISPLAY);
set_sched_policy(0, SP_FOREGROUND);
#ifdef ENABLE_CPUSETS
// Put most SurfaceFlinger threads in the system-background cpuset
// Keeps us from unnecessarily using big cores
// Do this after the binder thread pool init
set_cpuset_policy(0, SP_SYSTEM);
#endif
// initialize before clients can connect
//初始化
flinger->init();
// publish surface flinger
//加入ServiceManager中進行管理,最後啟動服務
sp<IServiceManager> sm(defaultServiceManager());
sm->addService(String16(SurfaceFlinger::getServiceName()), flinger, false);
// publish GpuService
sp<GpuService> gpuservice = new GpuService();
sm->addService(String16(GpuService::SERVICE_NAME), gpuservice, false);
// run surface flinger in this thread
flinger->run();
return 0;
}
在上面的原始檔中建立了一個SurfaceFlinger物件flinger,然後呼叫flinger->init()進行初始化,再將其加入ServiceManager中進行管理,最後啟動服務。
我們來看一下frameworks\native\services\surfaceflinger\SurfaceFlinger.cpp中的init()初始化函式:
void SurfaceFlinger::init() {
ALOGI( "SurfaceFlinger's main thread ready to run. "
"Initializing graphics H/W...");
{ // Autolock scope
Mutex::Autolock _l(mStateLock);
// initialize EGL for the default display
mEGLDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(mEGLDisplay, NULL, NULL);
// start the EventThread
sp<VSyncSource> vsyncSrc = new DispSyncSource(&mPrimaryDispSync,
vsyncPhaseOffsetNs, true, "app");
mEventThread = new EventThread(vsyncSrc, *this);
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, true, "sf");
mSFEventThread = new EventThread(sfVsyncSrc, *this);
mEventQueue.setEventThread(mSFEventThread);
// Get a RenderEngine for the given display / config (can't fail)
mRenderEngine = RenderEngine::create(mEGLDisplay,
HAL_PIXEL_FORMAT_RGBA_8888);
}
// Drop the state lock while we initialize the hardware composer. We drop
// the lock because on creation, it will call back into SurfaceFlinger to
// initialize the primary display.
mHwc = new HWComposer(this);
mHwc->setEventHandler(static_cast<HWComposer::EventHandler*>(this));
Mutex::Autolock _l(mStateLock);
// retrieve the EGL context that was selected/created
mEGLContext = mRenderEngine->getEGLContext();
LOG_ALWAYS_FATAL_IF(mEGLContext == EGL_NO_CONTEXT,
"couldn't create EGLContext");
// make the GLContext current so that we can create textures when creating
// Layers (which may happens before we render something)
getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
mEventControlThread = new EventControlThread(this);
mEventControlThread->run("EventControl", PRIORITY_URGENT_DISPLAY);
// initialize our drawing state
mDrawingState = mCurrentState;
// set initial conditions (e.g. unblank default device)
initializeDisplays();
// start boot animation
//呼叫startBootAnim()開始動畫顯示
startBootAnim();
ALOGV("Done initializing");
}
init()函式主要是對螢幕顯示視窗進行初始化工作。在最後呼叫了startBootAnim(),我們來看一下SurfaceFlinger.cpp中的startBootAnim()函式:
void SurfaceFlinger::startBootAnim() {
// start boot animation
property_set("service.bootanim.exit", "0");
property_set("ctl.start", "bootanim");
}
其中先將”service.bootanim.exit”標誌為 0,這個標誌用於在bootanim程序中檢測開機動畫是否結束,然後通過發”ctl.start”請求給init程序,啟動”bootanim”服務。
這裡需要注意的是,開機動畫程序在init.rc中註冊過了,但是在解析init.rc檔案時並沒有直接啟動,而是由SurfaceFlinger服務來啟動。這是因為init.rc 雖然註冊了bootanimation服務,但是被disabled了。所以在解析init.rc檔案時,bootanimation並沒有被直接啟動。
service bootanim /system/bin/bootanimation
class main
user root
group graphics
disabled
oneshot
由SurfaceFlinger啟動的原因是開機動畫必須得有顯示視窗,而這個顯示視窗則是由SurfaceFlinger提供的,所以必須得在SurfaceFlinger初始化完成之後,才開始顯示開機動畫。
下面轉到bootanimation,入口檔案為 frameworks/base/cmds/bootanimation/bootanimation_main.cpp :
int main()
{
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
char value[PROPERTY_VALUE_MAX];
property_get("debug.sf.nobootanimation", value, "0");
int noBootAnimation = atoi(value);
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;
}
啟動了一個執行緒池。BootAnimation物件在顯示第三個開機畫面的過程中,需要與SurfaceFlinger服務通訊,因此bootanimation就需要啟動一個Binder執行緒池。
這裡的BootAnimation類(frameworks\base\cmds\bootanimation\BootAnimation.cpp)間接地繼承了RefBase類,並且重寫了RefBase類的成員函式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物件boot第一次被智慧指標引用的時,物件的成員函式onFirstRef()會被呼叫。
此外BootAnimation類繼承了Thread類(\system\core\libutils\Threads.cpp):
BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true) {
mSession = new SurfaceComposerClient();
}
Thread 類有個run() 函式,在run()函式中會呼叫_threadLoop()函式,而_threadLoop函式又呼叫了readyToRun和threadLoop兩個函式:
do {
bool result;
if (first) {
first = false;
self->mStatus = self->readyToRun();
result = (self->mStatus == NO_ERROR);
if (result && !self->exitPending()) {
// Binder threads (and maybe others) rely on threadLoop
// running at least once after a successful ::readyToRun()
// (unless, of course, the thread has already been asked to exit
// at that point).
// This is because threads are essentially used like this:
// (new ThreadSubclass())->run();
// The caller therefore does not retain a strong reference to
// the thread and the thread would simply disappear after the
// successful ::readyToRun() call instead of entering the
// threadLoop at least once.
result = self->threadLoop();
}
} else {
result = self->threadLoop();
}
BootAnimation類重寫了readyToRun和threadLoop這兩個函式:
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;
// create the native surface
// 初始化opengl和繪圖表面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;
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;
// 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);
// 判斷是否有開機動畫檔案 bootanimation.zip 存在
if (encryptedAnimation && (access(getAnimationFileName(IMG_ENC), R_OK) == 0)) {
mZipFileName = getAnimationFileName(IMG_ENC);
}
else if (access(getAnimationFileName(IMG_OEM), R_OK) == 0) {
mZipFileName = getAnimationFileName(IMG_OEM);
}
else if (access(getAnimationFileName(IMG_SYS), R_OK) == 0) {
mZipFileName = getAnimationFileName(IMG_SYS);
}
return NO_ERROR;
}
bool BootAnimation::threadLoop()
{
bool r;
// We have no bootanimation file, so we use the stock android logo
// animation.
if (mZipFileName.isEmpty()) {
r = android();
} else {
r = movie();
}
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;
}
readyToRun() 主要是對opengl工作環境進行初始化,判斷使用者自定義的開機動畫檔案是否存在,儲存結果到mZipFileName 成員變數中。threadLoop 就開始真正的播放動畫了,當mZipFileName是空時,則播放Android系統預設的開機動畫,否則播放使用者自定義的開機動畫 。自定義的開機動畫是由檔案USER_BOOTANIMATION_FILE或者檔案SYSTEM_BOOTANIMATION_FILE來描述的。只要其中的一個檔案存在,那麼開機畫面就會使用使用者自定義的開機動畫。
android() 播放的是系統原生動畫,“android”字樣加上不斷移動的光影效果。movie() 則是讀取bootanimation.zip 中的幀動畫,一張一張的輪播,形成動畫效果。下面來分析下這Android系統動畫函式android(),它還是存在於BootAnimation.cpp檔案中:
//播放系統原生動畫
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;
}
使用一個while迴圈去繪圖,並在迴圈中呼叫 checkExit()判斷是否應該結束動畫。
checkExit()也存在於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();
if (mAudioPlayer != NULL) {
mAudioPlayer->requestExit();
}
}
}
檢測到 “service.bootanim.exit” 的值被修改成非 0 之後,就呼叫 requestExit() 結束動畫。
requestExit() 函式是父類 Thread 中定義的,在Threads.cpp檔案中:
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”。經過分析可知在\frameworks\native\services\surfaceflinger\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.
//將service.bootanim.exit的值設定為1
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)));
}
在frameworks\native\libs\gui\ISurfaceComposer.cpp中呼叫了bootFinished():
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;
}
……
}
}
現在來探究是誰傳送了BOOT_FINISHED 訊息給SurfaceFlinger 服務,檢視程式碼發現,在\frameworks\native\include\gui\ISurfaceComposer.h中有定義BOOT_FINISHED:
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,
……
}
……
}
發現在\services\core\java\com\android\server\wm\WindowManagerService.java中有傳送 IBinder::FIRST_CALL_TRANSACTION 訊息,並註釋說是 BOOT_FINISHED :
public 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();
}
……
}
獲取 SurfaceFlinger 服務,傳送了 IBinder.FIRST_CALL_TRANSACTION 訊息。
在WindowManagerService.java中的enableScreenAfterBoot()是開機流程中的服務:
public void enableScreenAfterBoot() {
synchronized(mWindowMap) {
if (DEBUG_BOOT) {
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.i(TAG_WM, "enableScreenAfterBoot: mDisplayEnabled=" + mDisplayEnabled
+ " mForceDisplayEnabled=" + mForceDisplayEnabled
+ " mShowingBootMessages=" + mShowingBootMessages
+ " mSystemBooted=" + mSystemBooted, here);
}
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();
}
在frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java又呼叫了 ActivityManagerService.enableScreenAfterBoot() :
final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
Configuration config) {
...
if (enableScreen) {
mService.enableScreenAfterBoot();
}
...
}
在WindowManagerService.java中呼叫了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);
}
而在ActivityThread 中又呼叫了ActivityManagerService.activityIdle():
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系統啟動完成後,就會啟動Launcher中的主Activity,一旦Launcher的Activity啟動完成之後,如果沒有使用者操作,就會進入空閒狀態,ActivityThread就會呼叫註冊的IdleHandler。然後層層轉發呼叫,最終呼叫SurfaceFlinger去終止開機動畫。
上面的分析是從下往上分析的,下面再從頂向下理一遍:
- Launcher啟動,註冊一個Idler空閒處理器到ActivityThread中;
- Launcher主執行緒空閒的時候就會呼叫Idler。queueIdle()方法;
- Idler.queueIdle() 通過Binder通訊呼叫了ActivityManagerService.activityIdle()方法
- 呼叫ActivityStackSupervisor.activityIdleInternalLocked()方法;
- 呼叫ActivityManagerService.enableScreenAfterBoot()方法;
- 呼叫WindowManagerService.enableScreenAfterBoot()方法;
- 呼叫WindowManagerService.performEnableScreen()方法;
- performEnableScreen 通過Binder通訊傳送 BOOT_FINISHED 訊息給 ISurfaceComposer
- 呼叫ISurfaceComposer 收到 BOOT_FINISHED 訊息後呼叫SurfaceFlinger::bootFinished 函式設定動畫結束標誌。
我們可以在各個方法呼叫點加Log來進行除錯。