Android系统开发进阶-BootAnimation启动与结束流程

开机动画 Android 系统在启动过程中播放的一段动画,其目的是给用户一个良好的开机体验。在开机过程中能看到一个动画效果表示系统正在启动中,肯定比一直黑屏等待系统启动完成体验好很多。下面我们从源码层面了解一下 BootAnimation 的启动流程。

1. bootanim 服务声明

BootAnimation 的源码路径为: Android/frameworks/base/cmds/bootanimation。进入这个目录后,我们看到有一个 bootanim.rc 文件,内容如下:

1
2
3
4
5
6
7
8
service bootanim /system/bin/bootanimation
class core animation
user graphics
group graphics audio
disabled
oneshot
ioprio rt 0
writepid /dev/stune/top-app/tasks

其中定义了一个 bootanim 服务, 但这个服务是被 disable 的状态,也就是 init 解析 init.rc 的时候不会自动启动这个服务。

再看 Android.bp :

1
2
3
4
5
6
cc_binary {
name: "bootanimation",
defaults: ["bootanimation_defaults"],
...
init_rc: ["bootanim.rc"],
}

有个 init_rc: ["bootanim.rc"] 的配置。加了这个配置之后,在编译系统的时候会把 bootanim.rc 文件安装到 /system/etc/init/ 目录。
init 进程在启动的时候会加载这个目录下的所有 rc 文件, 具体见代码路径 Android/system/core/init/init.cpp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);

std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
if (!parser.ParseConfig("/product_services/etc/init")) {
late_import_paths.emplace_back("/product_services/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/vendor/etc/init")) {
late_import_paths.emplace_back("/vendor/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}

至此,我们已经定义好了 bootanim 服务,但是并没有自动启动,接下来我们看看是怎么启动的。

2. bootanim 服务启动

我们先想一下为什么 bootanim 服务要定义成 disable 的?为什么不直接在 init 阶段启动?
因为动画是需要显示屏幕上的。如果图像显示服务那个时候还没有初始化完,那 BootAnimation 就没法播放动画了。所以 bootanim 服务必须要等图形显示相关的服务初始化完了才启动。在 Android 系统中,图像合成显示的服务是 surfaceflinger,源码路径为:Android/frameworks/native/services/surfaceflinger

我们进入 surfaceflinger 源码目录 grep 一下 :

1
2
3
4
5
6
7
8
9
qiushao@qiushao-pc:~/source/android-10/frameworks/native/services/surfaceflinger$ grep -nr bootanim
StartPropertySetThread.cpp:33: property_set("service.bootanim.exit", "0");
StartPropertySetThread.cpp:35: property_set("ctl.start", "bootanim");
tests/fakehwc/FakeComposerUtils.cpp:162: property_set("debug.sf.nobootanimation", "1");
tests/fakehwc/FakeComposerUtils.cpp:165: property_get("debug.sf.nobootanimation", value, "0");
tests/fakehwc/FakeComposerUtils.cpp:178: property_set("debug.sf.nobootanimation", "0");
SurfaceFlinger.cpp:562: property_set("service.bootanim.exit", "1");
EventLog/EventLogTags.logtags:39:60110 sf_stop_bootanim (time|2|3)
qiushao@qiushao-pc:~/source/android-10/frameworks/native/services/surfaceflinger$

发现在 StartPropertySetThread.cpp 中有启动 bootanim 相关的代码: property_set("ctl.start", "bootanim");
设置 ctl.start=bootanim 属性,即让 init 进程启动 bootanim 服务。

1
2
3
4
5
6
7
8
9
10
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;
}

再看 SurfaceFlinger.cpp 中的 SurfaceFlinger::init() 函数的一段代码:

1
2
3
4
5
6
7
8
9
10
// set initial conditions (e.g. unblank default device)
initializeDisplays();
getRenderEngine().primeCache();
// Inform native graphics APIs whether the present timestamp is supported:
const bool presentFenceReliable = !getHwComposer().hasCapability(HWC2::Capability::PresentFenceIsNotReliable);
mStartPropertySetThread = getFactory().createStartPropertySetThread(presentFenceReliable);

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

先调用 initializeDisplays(); 初始化显示系统。再调用 mStartPropertySetThread->Start() 启动 mStartPropertySetThread 线程:
mStartPropertySetThread->Start() –> mStartPropertySetThread->run() –> mStartPropertySetThread->threadLoop()。
bootanim 服务就这样在显示系统初始化完成后,马上被 SurfaceFlinger 服务启动了。

SurfaceFlinger 的源码目录下也有个 rc 文件 surfaceflinger.rc:

1
2
3
4
5
6
7
8
9
service surfaceflinger /system/bin/surfaceflinger
class core animation
user system
group graphics drmrpc readproc
onrestart restart zygote
writepid /dev/stune/foreground/tasks
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 没有被 disable, 那就是系统启动的时候, 由 init 进程自动启动的了。

3. BootAnimation 播放流程

BootAnimation 的执行入口为 bootanimation_main.cpp 的 main 函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int main()
{
setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
//是否支持开机动画, 由 ro.boot.quiescent 这个属性决定。
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());

//等待SurfaceFlinger初始化完成并加入到 ServiceManager。
//因为 bootanim 服务是在 SurfaceFlinger.init() 里面启动的,但在 SurfaceFlinger 服务是在执行完 SurfaceFlinger.init() 之后才加入到 ServiceManager 的,所以这里需要等一下。
waitForSurfaceFlinger();
//运行动画线程,开始播放动画。
boot->run("BootAnimation", PRIORITY_DISPLAY);
ALOGV("Boot animation set up. Joining pool.");
IPCThreadState::self()->joinThreadPool();
}
return 0;
}

BootAnimation 继承了 Thread, Thread 又继承了 RefBase , RefBase 有一个函数 onFirstRef(),这个函数在对象第一次被引用的时候会调用,这是 RefBase 引用计数的一个机制。 BootAnimation 类重写了这个函数。因此在执行 sp<BootAnimation> boot = new BootAnimation(audioplay::createAnimationCallbacks()); 这行的时候, BootAnimation.onFirstRef() 会被自动调用。这个函数里面会加载动画文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
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());
}
}

这里我有点不太明白,为什么要重写 onFirstRef,并在里面加载动画文件。为什么不直接在构造函数里面加载动画文件?绕这么一圈有什么意义呢?
下面的疑问暂且不管,我们来看看怎么加载动画文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static const char OEM_BOOTANIMATION_FILE[] = "/oem/media/bootanimation.zip";
static const char PRODUCT_BOOTANIMATION_DARK_FILE[] = "/product/media/bootanimation-dark.zip";
static const char PRODUCT_BOOTANIMATION_FILE[] = "/product/media/bootanimation.zip";
static const char SYSTEM_BOOTANIMATION_FILE[] = "/system/media/bootanimation.zip";
static const char APEX_BOOTANIMATION_FILE[] = "/apex/com.android.bootanimation/etc/bootanimation.zip";

bool BootAnimation::preloadAnimation() {
findBootAnimationFile();
if (!mZipFileName.isEmpty()) {
mAnimation = loadAnimation(mZipFileName);
return (mAnimation != nullptr);
}

return false;
}
...

void BootAnimation::findBootAnimationFile() {
...
const bool playDarkAnim = android::base::GetIntProperty("ro.boot.theme", 0) == 1;
static const char* bootFiles[] =
{APEX_BOOTANIMATION_FILE, playDarkAnim ? PRODUCT_BOOTANIMATION_DARK_FILE : PRODUCT_BOOTANIMATION_FILE,
OEM_BOOTANIMATION_FILE, SYSTEM_BOOTANIMATION_FILE};
static const char* shutdownFiles[] =
{PRODUCT_SHUTDOWNANIMATION_FILE, OEM_SHUTDOWNANIMATION_FILE, SYSTEM_SHUTDOWNANIMATION_FILE, ""};

for (const char* f : (!mShuttingDown ? bootFiles : shutdownFiles)) {
if (access(f, R_OK) == 0) {
mZipFileName = f;
return;
}
}
}

findBootAnimationFile() 函数的功能是根据优先级查找存在的动画文件,结果保存在 mZipFileName 中。bootFiles 是开机动画视频优先级列表, shutdownFiles 是关机动画优先级列表。这里我们只关心开机动画,其优先级如下:
APEX_BOOTANIMATION_FILE > PRODUCT_BOOTANIMATION_FILE > OEM_BOOTANIMATION_FILE > SYSTEM_BOOTANIMATION_FILE
对应的具体路径看开关的常量定义。

如果找到动画文件,则调用 loadAnimation 来加载动画文件。具体的逻辑我们这里就不关注了。

接下来回到 main 函数中, 等待 SurfaceFlinger 初始化完成。然后启动 BootAnimation 线程。这里又有一个比较绕的地方:BootAnimation 的父类 Thread 有一个虚函数 readyToRun。BootAnimation 重写了这个函数。在调用 Thread.run 函数启动线程的时候会先调用 readyToRun,然后再调用 threadLoop。
readyToRun 的主要工作是调用 SurfaceFlinger 服务创建 surface, 然后初始化 opengl(egl) 环境,这里我们也不用关注。 threadLoop 就是开始真正播放动画了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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();
}
...
}
bool BootAnimation::movie()
{
...
playAnimation(*mAnimation);
...
releaseAnimation(mAnimation);
...
}
bool BootAnimation::playAnimation(const Animation& animation)
{
...
for (size_t i=0 ; i<pcount ; i++) { //动画是分段的,依次播放各片段
...
for (int r=0 ; !part.count || r<part.count ; r++) { //每个片段循环播放次数
...
for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) { //一个片段的一次播放
//逐帧播放动画,这些 opengl 绘图细节我们不关注
...
//查检是否开机完成,可以退出动画了
checkExit();
}
usleep(part.pause * ns2us(frameDuration));
// For infinite parts, we've now played them at least once, so perhaps exit
if(exitPending() && !part.count && mCurrentInset >= mTargetInset)
break;
}
}
...
}
void BootAnimation::checkExit() {
// EXIT_PROP_NAME = "service.bootanim.exit"
char value[PROPERTY_VALUE_MAX];
property_get(EXIT_PROP_NAME, value, "0");
int exitnow = atoi(value);
if (exitnow) {
requestExit();
mCallbacks->shutdown();
}
}

我把其中绘图相关的代码都删除掉了,只保留了个逻辑框架。播放线程开始后,先判断是否有动画文件存在,有的话,则调用 movie() 函数来播放动画。没有的话,则调用 android()函数来显示一个 android logo。
movie 又调用了 playAnimation() 函数来做真正的播放工作。动画的分段,循环播放逻辑已经在上面的代码上注释了。在播放每一帧的最后都会调用 checkExit() 函数来检查是否要退出动画了。
checkExit() 函数里面通过获取 service.bootanim.exit 这个属性值来判断是否要退出动画。如果这个属性被设置为 “1”, 则调用 requestExit() 请求结束线程, exitPending()函数的返回值就会变成 true。就退出播放线程。BootAnimation 的使命就完成了,可以退出了。

4. bootanim 服务结束

经过上面的流程分析,我们知道了 BootAnimation 通过 service.bootanim.exit 这个属性来退出动画播放的。那这个属性是谁设置的呢?泛化点来说,我们怎么分析确定一条又长又复杂的调用链呢?
我的方法是 grep + 堆栈打印。有了这两把梭,基本上大部分问题都能搞定。一般能用 grep 就用 grep 先,对于比较简单的调用链来说,grep 就已经能确定了。只有遇到调用关系比较乱,屡不清关系,才用堆栈打印。下面我们就用这两把梭来试试。
先在 framework 目录下 grep :

1
2
3
4
5
6
7
8
9
qiushao@qiushao-pc:~/source/android-10/frameworks$ grep -nr "service.bootanim.exit"
base/core/proto/android/os/system_properties.proto:522: optional int32 service_bootanim_exit = 23;
base/cmds/bootanimation/BootAnimation.cpp:98:static const char EXIT_PROP_NAME[] = "service.bootanim.exit";
base/cmds/bootanimation/FORMAT.md:104:the system property `service.bootanim.exit` to a nonzero string.)
base/cmds/bootanimation/iot/iotbootanimation_main.cpp:80: property_get("service.bootanim.exit", value, "0");
base/services/core/java/com/android/server/wm/WindowManagerService.java:3312: SystemProperties.set("service.bootanim.exit", "1");
native/services/surfaceflinger/StartPropertySetThread.cpp:33: property_set("service.bootanim.exit", "0");
native/services/surfaceflinger/SurfaceFlinger.cpp:562: property_set("service.bootanim.exit", "1");
qiushao@qiushao-pc:~/source/android-10/frameworks$

我们发现有两处地方设置了 service.bootanim.exit = 1。WindowManagerService.java 和 SurfaceFlinger.cpp。
这两条路我们分别分析,先看 c++ 层的调用:

1
2
3
4
5
6
7
void SurfaceFlinger::bootFinished()
{
...
// 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");

那 bootFinished 函数是由谁来调用的呢?我们在 framework 目录下 grep 一下看看:

1
2
3
4
5
6
7
8
9
qiushao@qiushao-pc:~/source/android-10/frameworks$ grep -nr bootFinished
native/services/surfaceflinger/SurfaceFlinger.cpp:532:void SurfaceFlinger::bootFinished()
native/services/surfaceflinger/SurfaceFlinger.h:409: void bootFinished() override;
native/libs/gui/include/gui/ISurfaceComposer.h:149: virtual void bootFinished() = 0;
native/libs/gui/tests/Surface_test.cpp:568: void bootFinished() override {}
Binary file native/libs/gui/.ISurfaceComposer.cpp.swp matches
native/libs/gui/ISurfaceComposer.cpp:104: virtual void bootFinished()
native/libs/gui/ISurfaceComposer.cpp:1058: bootFinished();
qiushao@qiushao-pc:~/source/android-10/frameworks$

看起来是 ISurfaceComposer.cpp 里面调用的:

1
2
3
4
5
6
7
8
9
10
11
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;
}
...

那又是谁通过 binder 调用了 BOOT_FINISHED 这个指令呢?继续在 framework 目录下 grep

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
qiushao@qiushao-pc:~/source/android-10/frameworks$ grep -nr BOOT_FINISHED
base/services/core/java/com/android/server/wm/WindowManagerService.java:3327: surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
native/services/surfaceflinger/SurfaceFlinger.cpp:5189: case BOOT_FINISHED:
native/libs/gui/include/gui/ISurfaceComposer.h:448: // Note: BOOT_FINISHED must remain this value, it is called from
native/libs/gui/include/gui/ISurfaceComposer.h:450: BOOT_FINISHED = IBinder::FIRST_CALL_TRANSACTION,
native/libs/gui/ISurfaceComposer.cpp:108: remote()->transact(BnSurfaceComposer::BOOT_FINISHED, data, &reply);
native/libs/gui/ISurfaceComposer.cpp:1056: case BOOT_FINISHED: {
qiushao@qiushao-pc:~/source/android-10/frameworks$
```

看起来是 WindowManagerService.java 里面调用的:
```java
private void performEnableScreen() {
synchronized (mGlobalLock) {
...
if (!mBootAnimationStopped) {
Trace.asyncTraceBegin(TRACE_TAG_WINDOW_MANAGER, "Stop bootanim", 0);
// 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.
SystemProperties.set("service.bootanim.exit", "1");
mBootAnimationStopped = true;
}
if (!mForceDisplayEnabled && !checkBootAnimationCompleteLocked()) {
if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: Waiting for anim complete");
return;
}
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!");
}
...

在 performEnableScreen 这个函数中通过 binder 调用了 surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, data, null, 0);
而实际上, IBinder.FIRST_CALL_TRANSACTION == BOOT_FINISHED, 所以也就是相当于调用了 BOOT_FINISHED。这里的代码风格就有点问题了,如果没有注释的话,估计就找不下去了。

我们再细心看的话,会发现其实这里已经直接设置了 SystemProperties.set("service.bootanim.exit", "1");,这里就是我们最开始使用 grep “service.bootanim.exit” 时,得到的另一条线路。也就是 c++, java 这两条路都汇集到这里了。那 java 层都已经设置了,其实 c++ 层设置与否都没关系了。

我们继续顺藤摸瓜,看看是谁调用了 performEnableScreen :

1
2
3
4
5
6
7
8
9
10
11
qiushao@qiushao-pc:~/source/android-10/frameworks$ grep -nr performEnableScreen
base/services/core/java/com/android/server/wm/WindowManagerService.java:3235: performEnableScreen();
base/services/core/java/com/android/server/wm/WindowManagerService.java:3271: performEnableScreen();
base/services/core/java/com/android/server/wm/WindowManagerService.java:3281: private void performEnableScreen() {
base/services/core/java/com/android/server/wm/WindowManagerService.java:3283: if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: mDisplayEnabled=" + mDisplayEnabled
base/services/core/java/com/android/server/wm/WindowManagerService.java:3317: if (DEBUG_BOOT) Slog.i(TAG_WM, "performEnableScreen: Waiting for anim complete");
base/services/core/java/com/android/server/wm/WindowManagerService.java:3394: performEnableScreen();
base/services/core/java/com/android/server/wm/WindowManagerService.java:4734: performEnableScreen();
base/services/core/java/com/android/server/wm/WindowManagerService.java:4878: performEnableScreen();
base/services/art-profile:17483:HSPLcom/android/server/wm/WindowManagerService;->performEnableScreen()V
qiushao@qiushao-pc:~/source/android-10/frameworks$

看起来有好几个调用,稍微看了下这几个调用的上下文,还真不好确定是哪一处调用。这时候就该我们的第二把梭 堆栈打印 出场了。我们在 performEnableScreen 函数的入口添加一行打印:

1
2
private void performEnableScreen() {
Slog.d(TAG_WM, "qiushao performEnableScreen", new Throwable());

然后重新编译运行一下,看看 log, 找第一个 “qiushao performEnableScreen”:

1
2
3
4
5
6
7
8
9
10
11
12
02-24 20:33:49.960  1657  1698 D WindowManager: qiushao performEnableScreen
02-24 20:33:49.960 1657 1698 D WindowManager: java.lang.Throwable
02-24 20:33:49.960 1657 1698 D WindowManager: at com.android.server.wm.WindowManagerService.performEnableScreen(WindowManagerService.java:3282)
02-24 20:33:49.960 1657 1698 D WindowManager: at com.android.server.wm.WindowManagerService.enableScreenAfterBoot(WindowManagerService.java:3235)
02-24 20:33:49.960 1657 1698 D WindowManager: at com.android.server.wm.ActivityTaskManagerService$LocalService.enableScreenAfterBoot(ActivityTaskManagerService.java:6482)
02-24 20:33:49.960 1657 1698 D WindowManager: at com.android.server.wm.ActivityTaskManagerService.lambda$postFinishBooting$6$ActivityTaskManagerService(ActivityTaskManagerService.java:5678)
02-24 20:33:49.960 1657 1698 D WindowManager: at com.android.server.wm.-$$Lambda$ActivityTaskManagerService$oP6xxIfnD4kb4JN7aSJU073ULR4.run(Unknown Source:6)
02-24 20:33:49.960 1657 1698 D WindowManager: at android.os.Handler.handleCallback(Handler.java:883)
02-24 20:33:49.960 1657 1698 D WindowManager: at android.os.Handler.dispatchMessage(Handler.java:100)
02-24 20:33:49.960 1657 1698 D WindowManager: at android.os.Looper.loop(Looper.java:214)
02-24 20:33:49.960 1657 1698 D WindowManager: at android.os.HandlerThread.run(HandlerThread.java:67)
02-24 20:33:49.960 1657 1698 D WindowManager: at com.android.server.ServiceThread.run(ServiceThread.java:44)

看上面的堆栈信息,可以知道大概的流程是这样的:
ActivityTaskManagerService.java:5678 postFinishBooting –>
ActivityTaskManagerService.java:6482 enableScreenAfterBoot –>
WindowManagerService.java:3235 enableScreenAfterBoot –>
WindowManagerService.java:3282 performEnableScreen –>

我们追踪到 ActivityTaskManagerService.java postFinishBooting 这里就没线索了。只能继续用 grep 大法:

1
2
3
4
5
6
qiushao@qiushao-pc:~/source/android-10/frameworks$ grep -nr postFinishBooting
base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java:1261: mService.postFinishBooting(booting, enableScreen);
base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java:5672: void postFinishBooting(boolean finishBooting, boolean enableScreen) {
base/services/art-profile:15371:HSPLcom/android/server/wm/ActivityTaskManagerService;->lambda$postFinishBooting$6$ActivityTaskManagerService(ZZ)V
base/services/art-profile:15394:HSPLcom/android/server/wm/ActivityTaskManagerService;->postFinishBooting(ZZ)V
qiushao@qiushao-pc:~/source/android-10/frameworks$

只有一个地方有调用: ActivityStackSupervisor.java:1261 mService.postFinishBooting(booting, enableScreen);, 不知道还有多少层级的调用,懒得慢慢 grep 分析了,直接打印堆栈:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private boolean checkFinishBootingLocked() {
Slog.d(TAG, "qiushao checkFinishBootingLocked", new Throwable());
final boolean booting = mService.isBooting();
boolean enableScreen = false;
mService.setBooting(false);
if (!mService.isBooted()) {
mService.setBooted(true);
enableScreen = true;
}
if (booting || enableScreen) {
mService.postFinishBooting(booting, enableScreen);
}
return booting;
}

重新编译运行一下,看看 log:

1
2
3
4
5
6
7
8
02-24 21:08:03.958  1593  2392 D ActivityTaskManager: qiushao checkFinishBootingLocked
02-24 21:08:03.958 1593 2392 D ActivityTaskManager: java.lang.Throwable
02-24 21:08:03.958 1593 2392 D ActivityTaskManager: at com.android.server.wm.ActivityStackSupervisor.checkFinishBootingLocked(ActivityStackSupervisor.java:1253)
02-24 21:08:03.958 1593 2392 D ActivityTaskManager: at com.android.server.wm.ActivityStackSupervisor.activityIdleInternalLocked(ActivityStackSupervisor.java:1310)
02-24 21:08:03.958 1593 2392 D ActivityTaskManager: at com.android.server.wm.ActivityTaskManagerService.activityIdle(ActivityTaskManagerService.java:1675)
02-24 21:08:03.958 1593 2392 D ActivityTaskManager: at android.app.IActivityTaskManager$Stub.onTransact(IActivityTaskManager.java:1957)
02-24 21:08:03.958 1593 2392 D ActivityTaskManager: at android.os.Binder.execTransactInternal(Binder.java:1021)
02-24 21:08:03.958 1593 2392 D ActivityTaskManager: at android.os.Binder.execTransact(Binder.java:994)

根据堆栈信息,调用流程大概是这样的:
IActivityTaskManager binder 调用 –>
ActivityTaskManagerService.java:1675 activityIdle –>
ActivityStackSupervisor.java:1310 activityIdleInternalLocked –>
ActivityStackSupervisor.java:1253 checkFinishBootingLocked

线索到 ActivityTaskManagerService.activityIdle 这里又断了,只能用 grep 了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
qiushao@qiushao-pc:~/source/android-10/frameworks$ jgrep activityIdle
./base/core/java/android/app/ActivityThread.java:2060: am.activityIdle(a.token, a.createdConfig, stopProfiling);
./base/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java:43: private long mInactivityIdleThreshold;
./base/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java:70: mInactivityIdleThreshold = context.getResources().getInteger(
./base/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java:71: com.android.internal.R.integer.config_jobSchedulerInactivityIdleThreshold);
./base/services/core/java/com/android/server/job/controllers/idle/DeviceIdlenessTracker.java:148: final long when = nowElapsed + mInactivityIdleThreshold;
./base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java:1269: final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
./base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java:1282: if (DEBUG_IDLE) Slog.d(TAG_IDLE, "activityIdleInternalLocked: Callers="
./base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java:1356: "activityIdleInternalLocked");
./base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java:1764: activityIdleInternalLocked(null, false /* fromTimeout */,
./base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java:2601: void activityIdleInternal(ActivityRecord r, boolean processPausingActivities) {
./base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java:2603: activityIdleInternalLocked(r != null ? r.appToken : null, true /* fromTimeout */,
./base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java:2633: activityIdleInternal((ActivityRecord) msg.obj,
./base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java:2638: activityIdleInternal((ActivityRecord) msg.obj,
./base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java:1666: public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
./base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java:1675: final ActivityRecord r = mStackSupervisor.activityIdleInternalLocked(token,
qiushao@qiushao-pc:~/source/android-10/frameworks$

这里我们用了 jgrep, 需要 source build/envsetup.sh; lunch 之后才可以用这个命令。jgrep 只 grep java 源文件。
看 jgrep 的结果,只有 ActivityThread.java:2060: am.activityIdle(a.token, a.createdConfig, stopProfiling); 是我们要找的:

1
2
3
4
5
6
7
8
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;

Idler 是 ActivityThread 的一个内部类,我们看年这个类是在哪里使用的:

1
2
3
4
5
6
7
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
String reason) {
...
final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
...
Looper.myQueue().addIdleHandler(new Idler());
}

ActivityThread 是应用的 UI 线程,也就是主线程。handleResumeActivity 会调用 Acitvity类的 OnResume方法。Looper 我们可以理解为消息队列。在消息队列空闲的时候,会调用其中的 Idler Handler。而我们系统第一个起来的应用就是 Launcher。
至此,我基本上理清楚 BootAnimation 的结束流程了:
Launcher 启动完成, 消息队列 Looper 进入空闲状态 –>
Idler Handler –>
ActivityTaskManagerService.java:1675 activityIdle –>
ActivityStackSupervisor.java:1310 activityIdleInternalLocked –>
ActivityStackSupervisor.java:1253 checkFinishBootingLocked –>
ActivityStackSupervisor.java:1261 mService.postFinishBooting(booting, enableScreen); –>
ActivityTaskManagerService.java:5678 postFinishBooting –>
ActivityTaskManagerService.java:6482 enableScreenAfterBoot –>
WindowManagerService.java:3235 enableScreenAfterBoot –>
WindowManagerService.java:3282 performEnableScreen –>
SystemProperties.set(“service.bootanim.exit”, “1”);

这条路真是够复杂的。不知道为什么要设计得这么复杂。最后借用网上的一张图来总结一下,虽然这图与 Android 10 上的流程有点出入,还有一些细节不到位,但整体上是 OK 的:

BootAnimation启动流程

开机动画的启动与结束流程我们已经分析完了,其中的逻辑复杂混乱,但我们有两把梭: grep + 堆栈打印, grep 可以快刀斩乱麻,堆栈打印可以抽丝剥茧。希望大家能够学会灵活运用。
这个例子里面我们只在 java 层代码分析时使用了堆栈打印,其实在 c++ 层代码也是可以使用堆栈打印这个技巧的。关键字: anrdroid callstack