基于Android10.0,分析BOOT_COMPLETED的发送流程
一、概述
开机广播在很多应用中都会用到,用来启动应用程序,下面将介绍开机广播的广播过程。这个过程比较复杂,需要和AcitvityManagerService、WindowManagerService、PackageManagerService等交互,其具体的时序图如下。
二、开机广播启动过程
在AMS启动那篇文章中已经讲到桌面如何的启动,在桌面启动完成后即桌面Activity onResume之后,就会发送开机广播。桌面Activity onResume阶段,执行了handleResumeActivity方法,见Activity启动过程。handleResumeActivity中加载完window之后将自己实现的IdleHandler添加到自己的消息队列中。
1.1 AT.handleResumeActivity
[->ActivityThread.java]
1 | @Override |
1.2 MQ.addIdleHandler
[->MessageQueue.java]
1 | public void addIdleHandler(@NonNull IdleHandler handler) { |
将IdleHandler加入到消息队列,当消息队列空闲的时候执行idler.queueIdle()的回调。
1.3 Idler.queueIdle
[->ActivityThread.java]
1 | private class Idler implements MessageQueue.IdleHandler { |
1.4 AMS.activityIdle
[->ActivityManagerService.java]
1 | @Override |
1.5 ASS.activityIdleInternalLocked
[->ActivityStackSupervisor.java]
1 | // Checked. |
这个和开机广播相关的是检查是否还在开机阶段。如果桌面启动完成,开机动画就结束了。
1.6 ASS.checkFinishBootingLocked
[->ActivityStackSupervisor.java]
1 | /** |
mService.mBooting是在ASM.systemReady中设置为true的,这里会修改 mService.mBooting为false;这里由于booting为true,所以会执行postFinishBooting方法。
1.7 AMS.postFinishBooting
[->ActivityManagerService.java]
1 | void postFinishBooting(boolean finishBooting, boolean enableScreen) { |
1 | public void handleMessage(Message msg) { |
这里Message要做两件事情finishBooting和enableScreenAfterBoot
1.8 AMS.finishBooting
[->ActivityManagerService.java]
1 | final void finishBooting() { |
第一次进来时mBootAnimationComplete是为false的,只有动画完成了才会回调方法bootAnimationComplete设置为true,所以这里直接返回了,这里再看下第二件事情enableScreenAfterBoot。
1.9 AMS.enableScreenAfterBoot
[->ActivityManagerService.java]
1 | void enableScreenAfterBoot() { |
[->WindowManagerService.java]
1 | public void enableScreenAfterBoot() { |
mSystemBooted初始值为false,这里会设置成true。mShowingBootMessages为true时改成false,然后设置一个30s的延迟消息,随后调用systemBooted方法,通知keyguard开机完成,最后执行performEnableScreen方法。
1.9.1 WMS.hideBootMessagesLocked
[->WindowManagerService.java]
1 | public void hideBootMessagesLocked() { |
1.9.2 PWM.hideBootMessages
[->PhoneWindowManager.java]
1 | /** {@inheritDoc} */ |
这个过程主要是隐藏开机过程中显示的Android系统正在启动或者Android系统正在升级的dialog提示。
该dialog的启动是在SystemServer中启动PKMS初始化开始的,可以参考文章PKMS的启动过程。
在启动过程中会执行mPackageManagerService.updatePackagesIfNeeded方法,其中有performDexOptUpgrade方法。
这个方法主要是对package进行dexoat升级。
1 | private int[] performDexOptUpgrade(List<PackageParser.Package> pkgs, boolean showDialog, |
调用显示正在开机的dialog。
[->WindowManagerService.java]
1 | public void showBootMessage(final CharSequence msg, final boolean always) { |
下面正式显示dialog,mShowingBootMessages这里设置成true。
[->PhoneWindowManager.java]
1 | /** {@inheritDoc} */ |
最后看下handleHideBootMessage,这里会取消dialog显示
1 | private void handleHideBootMessage() { |
1.9.3 PWM.systemBooted
[->PhoneWindowManager.java]
1 | /** {@inheritDoc} */ |
1.9.4 mH.sendEmptyMessageDelayed
[->WindowManagerService.java]
发送消息,最后执行performBootTimeout方法,可以看到performBootTimeout最后执行的是performEnableScreen方法。
1 | @Override |
1 | public void performBootTimeout() { |
1.10 WMS.performEnableScreen
[->WindowManagerService.java]
1 | private void performEnableScreen() { |
这里主要的工作是停止开机动画,通知SurfaceFlinger开机结束等。在停止开机动画前会有很多的判断,如果现有的window(桌面,状态栏,keyguard,壁纸等)都已经绘制完成包括,才会停止动画。
停止动画是执行了SystemProperties.set(“service.bootanim.exit”, “1”);在system/bin/bootanimation一般会在显示的时候循环查询这个prop的值,如果变成了1则退出。
1.10.1 PWM.canDismissBootAnimation
[->PhoneWindowManager.java]
1 | @Override |
mKeyguardDrawComplete是Keyguard是否绘制完成,绘制完成才会进入下面的方法。
1.10.2 checkWaitingForWindows
[->DisplayContent.java]
1 | boolean checkWaitingForWindows() { |
1.10.3 WMS.checkWaitingForWindows
[->WindowManagerService.java]
1 | private boolean checkBootAnimationCompleteLocked() { |
这里判断开机动画是否停止通过判断BOOT_ANIMATION_SERVICE服务是否在运行。
1.10.4 WMS.enableScreenIfNeeded
performEnableScreen里面并不能一次就能停止动画,查看日志:
1 | 01-01 22:00:26.180 2496 3127 I WindowManager: skytoby performEnableScreen: mDisplayEnabled=false mForceDisplayEnabled=false mShowingBootMessages=false mSystemBooted=true mOnlyCore=false |
除了从Launch发起的流程调用这里之外,还有其他的调用点,在WMS里面经过enableScreenIfNeeded方法发送ENABLE_SCREEN 消息给Handler进行处理。
[->WindowManagerService.java]
1 | @Override |
enableScreenIfNeededLocked这个方法也调用的十分的频繁,调用点也有多个位置,大部分的调用栈如下,在WMS每次进行performSurfacePlacement等主要是完成绘制布局之后检查调用。在发送ENABLE_SCREEN消息之前会判断mDisplayEnabled是否为true,如果通过performEnableScreen设置为true,则不再执行,避免了重复操作。
1 | 10-08 08:43:06.756 1592 2422 I WindowManager: enableScreenIfNeededLocked: mDisplayEnabled=false mForceDisplayEnabled=false mShowingBootMessages=false mSystemBooted=true |
1.11 AMS.bootAnimationComplete
[->ActivityManagerService.java]
1 | @Override |
在1.8节中mCallFinishBooting设置成true了,所以这里会执行finishBooting方法。通过bootAnimationComplete这个回调方法,再一次进入到了finishBooting方法。
1.12 AMS.finishBooting
[->ActivityManagerService.java]
1 | final void finishBooting() { |
这里主要的工作是设置abi开机完成的flag,注册ACTION_QUERY_PACKAGE_RESTART和ACTION_DELETE_DUMPHEAP广播,通知系统服务开机启动完成,开启onhold进程,而后发送开机广播。
1.13 UC.sendBootCompleted
[->UserController.java]
1 | void sendBootCompleted(IIntentReceiver resultTo) { |
1.14 UC.finishUserBoot
[->UserController.java]
1 | private void finishUserBoot(UserState uss, IIntentReceiver resultTo) { |
1.15 UC.maybeUnlockUser
[->UserController.java]
1 | /** |
1.16 UC.maybeUnlockUser
[->UserController.java]
1 | private boolean unlockUserCleared(final int userId, byte[] token, byte[] secret, |
1.17 UC.finishUserUnlocking
[->UserController.java]
1 | /** |
通过mHandler发送到AMS的主Handler处理,由于UserController继承了Handler.Callback,其handleMessage就在UserController类中。
1.18 UC.handleMessage
[->UserController.java]
1 | public boolean handleMessage(Message msg) { |
首先unlockUser,加载RecentTask
1.19 UC.finishUserUnlocked
[->UserController.java]
1 | /** |
1.20 UC.finishUserUnlockedCompleted
[->UserController.java]
1 | private void finishUserUnlockedCompleted(UserState uss) { |
到这里已经发送了ACTION_BOOT_COMPLETED广播。
三、总结
从开机完成启动到发送开机广播,其中的流程有很多,之前的文章有讲过systemserver启动,AMS、PMS的启动,同时还有Activity和广播的启动,综合着一些再来看这个过程就相对轻松些,同时通过编译版本调试打印出日志,更加证明了分析流程的正确性。
1.在Launch界面resume阶段,会向主线程消息队列放入new Idler(),在消息队列空闲的时候,就会调用。
2.通过Binder机制执行AMS的activityIdle,这个方法里面会检查是否完成开机。
3.调用finishBooting方法,来完成开机。在这个过程中第一次进入由于开机动画还没有停止从而会直接返回。
4.调用performEnableScreen方法,确定所有的窗口都已经绘制完成,包括Launch,状态栏,壁纸,同时要求开机动画停止,在等待开机动画停止的过程中,有一个30s的超时,同时还有其他的入口来调用WMS的performEnableScreen方法,当动画包停止后通过回调再次执行finishBooting方法。
5.在对credential-encrypted storage解锁后就正式的发送开机广播BOOT_COMPLETED。
附录
源码路径
1 | frameworks/base/core/java/android/app/ActivityThread.java |