在Android M及之前,当开机启动到锁屏界面时,所有程序阻塞,等待用户解锁(即使未设置开机密码,也需要滑屏解锁)后才会继续。
而Android 7.0引入了Direct Boot模式,当手机已经通电开机但是用户并有解锁锁屏的时候,Android N运行于一个安全的模式,也就是Dierect Boot模式。
而Direct Boot模式下,仅限于运行一些关键的、紧急的APP,比如:
广播
Android N在引入DBM后,也新增了一条ACTION_LOCKED_BOOT_COMPLETED广播:
Intent.ACTION_LOCKED_BOOT_COMPLETED
// 未解锁Intent.ACTION_BOOT_COMPLETED
应用获取解锁的通知:
在Direct Boot模式下,应用只能访问其他支持DirectBoot的应用和组件;如果依赖外部服务或Activity,需要妥善处理外部服务或Activity不可用的情形;默认情况下,Intent过滤器仅匹配当前状态(已解锁/未解锁)下可用的组件。
在Android N里,在启动Launcher之前会先启动一个FallbackHome;
FallbackHome是Settings里的一个activity,Settings的android:directBootAware
为true,而且FallbackHome在category中配置了Home属性;而Launcher的android:directBootAware
为false,所以在DirectBoot模式下,只有FallbackHome可以启动。即先启动com.android.settings/.FallbackHome ,待用户解锁后再启动com.android.launcher3/.Launcher。
所以,在ActivityManagerService在启动Home界面时,从PackageManagerService中获取到的就是FallbackHome。参考getHomeIntent()
和startHomeActivityLocked()
等函数。
阅读FallbackHome源码:packages/apps/Settings/src/com/android/settings/FallbackHome.java
FallbackHome是个透明的Activity,其代码不足100行。FallbackHome的onCreate()
方法里面会注册监听ACTION_USER_UNLOCKED广播,并调用maybeFinish()
方法。
而在ACTION_USER_UNLOCKED广播的BroadcastReceiver里面,当此广播到来,也是调用maybeFinish()
方法。
关键的maybeFinish()
方法:
注意两点:
如下是maybeFinish()
的源码:
private void maybeFinish() {
if (getSystemService(UserManager.class).isUserUnlocked()) {
final Intent homeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME);
final ResolveInfo homeInfo = getPackageManager().resolveActivity(homeIntent, 0);
if (Objects.equals(getPackageName(), homeInfo.activityInfo.packageName)) {
Log.d(TAG, "User unlocked but no home; let's hope someone enables one soon?");
mHandler.sendEmptyMessageDelayed(0, 500);
} else {
Log.d(TAG, "User unlocked and real home found; let's go!");
finish();
}
}
}
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
maybeFinish();
}
};
附录:
看一下第一次启动HomeActivity(即FallbackHome)的调用栈:
at com.android.server.am.ActivityStack.resumeTopActivityInnerLocked(ActivityStack.java:2597)
at com.android.server.am.ActivityStack.resumeTopActivityUncheckedLocked(ActivityStack.java:2127)
at com.android.server.am.ActivityStackSupervisor.resumeFocusedStackTopActivityLocked(ActivityStackSupervisor.java:1830)
at com.android.server.am.ActivityStarter.startActivityUnchecked(ActivityStarter.java:1249)
at com.android.server.am.ActivityStarter.startActivityLocked(ActivityStarter.java:516)
at com.android.server.am.ActivityStarter.startHomeActivityLocked(ActivityStarter.java:642)
at com.android.server.am.ActivityManagerService.startHomeActivityLocked(ActivityManagerService.java:3969)
at com.android.server.am.ActivityManagerService.systemReady(ActivityManagerService.java:13384)
at com.android.server.SystemServer.startOtherServices(SystemServer.java:1318)
at com.android.server.SystemServer.run(SystemServer.java:333)
at com.android.server.SystemServer.main(SystemServer.java:218)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:708)
注意:关注ActivityStarter.computeStackFocus()
函数,它决定了Activity在哪个stack上启动!
在开机接近尾声,WindowManagerService会调用enableScreenIfNeededLocked()
函数判断是否enable screen,通过Handler发生ENABLE_SCREEN消息到主线程。而在mH的handleMessage()中处理ENABLE_SCREEN消息时,会调用performEnableScreen()函数。
WindowManagerService.performEnableScreen()
函数中判断是否要enable screen的两个因素:
service.bootanim.exit
属性后等待开机动画结束;插叙:关于checkBootAnimationCompleteLocked()的内容:
注:checkBootAnimationCompleteLocked()向我们展示了通过Handler机制polling轮询的方法,这比while循环的方式更有效率,能够改善用户响应能力。
WindowManagerService.performEnableScreen()还做了如下动作:
service.bootanim.exit
属性为1)IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHED
data, null, 0);
data.recycle();
mDisplayEnabled=true
InputMonitor.setEventDispatchingLw()
// Enable input dispatchActivityManagerService.bootAnimationComplete()
ActivityManagerService.finishBooting()
;PhoneWindowManager.enableScreenAfterBoot()
updateRotationUnchecked()
// 疑问:如何强制横屏显示?继续:ActivityManagerService.bootAnimationComplete()
函数
这里的关键是AMS的finishBooting()函数,它做了啥:
SystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED)
函数;
UserController.sendBootCompletedLocked()
函数;UserController类负责管理用户状态,如下:
上述函数是依次被调用的关系:
UserController.sendBootCompletedLocked()
调用finishUserBoot()
;finishUserBoot()
会调用maybeUnlockUser(),进而调用到finishUserUnlocking()
;finishUserUnlocking()
会向mHandler发送SYSTEM_USER_UNLOCK_MSG消息;finishUserUnlocked()
函数;而且,UserController类在不同阶段会发送不同广播:
UserController.finishUserBoot()
会发送Intent.ACTION_LOCKED_BOOT_COMPLETED
广播;UserController.finishUserUnlocked()
会发送Intent.ACTION_USER_UNLOCKED
广播;UserController.finishUserUnlockedCompleted()
会发送Intent.ACTION_BOOT_COMPLETED
广播;OK,我们再回头看下FallbackHome,当收到ACTION_USER_UNLOCKED广播,它就会调用maybeFinish()
方法去启动Launcher!
注意:UserController不是一个独立模块,它是ActivityManager的一部分!
疑问:怎么没看到用户解锁的处理过程?
上面提到的InputMonitor.setEventDispatchingLw()
会通过InputManagerService.setInputDispatchMode()
最终调用Native层的InputDispatcher::setInputDispatchMode()
函数。
Native层的InputDispatcher::setInputDispatchMode()
函数,它就设置两个flag变量:
可以在shell里通过dumpsys input | grep DispatchEnabled
命令来查看此变量的值。
在InputDispatcher::dispatchOnceInnerLocked()
函数分发Input事件时,会判断mDispatchEnabled和mDispatchFrozen这俩变量的值:
引入Direct Boot Mode后,由于FallbackHome的原因,开机时间明显变长,如何优化?
以下内容摘录自Android7.0 DirectBoot阻塞开机分析:
Android 7.0新增了DirectBoot功能,AOSP中为实现该功能修改了开机代码流程,并且这部分流程并未根据设备是否支持DirectBoot做区分,只是流程上做了兼容,确保不支持DirectBoot的设备在这套流程下也能正常开机。
在这套流程下,用户解锁后才可进入非directBootAware应用,包括Launcher。
com.android.settings/.FallbackHome中判断用户解锁状态,已解锁才会Finish掉去启动Launcher,未解锁就等待ACTION_USER_UNLOCKED广播后再去启动Launcher。非DirectBoot模式下耗时4s就是在等待finishBooting后的系统广播ACTION_USER_UNLOCKED。
目前已从APP和PackageManagerService的角度尝试修改,在开机流程中绕过FallbackHome,但验证失败:
1)去除FallbackHome的android.intent.category.Home属性会导致停留在开机动画之后的界面。因为此时仍旧处于未解锁状态,且Launcher非directBootAware应用,PMS中的限制导致此时无法启动Launcher;
2)修改FallbackHome和Launcher的优先级仍旧先启动FallbackHome;
3)将Launcher标记为directBootAware应用会导致开机后Launcher crash。因为Launcher中的widget仍旧是非directBootAware的,此时仍旧无法启动,除非将widget相关的APP都标记为directBootAware;
4)PMS依赖手机当前的状态,需要user解锁才能正常查询。如果强制修改,不考虑DirectBoot和当前启动状态,即使当前user未解锁,依然可以查询符合条件的component,修改后会有无法开机的现象。因为Launcher不是directBootAware的,当前手机user尚未解锁,涉及存储相关的解锁也未进行。
开机绕过FallbackHome涉及的修改面很多,并非通过修改APP或PMS可以实现,还涉及存储区域解锁以及用户状态和ACTION_USER_UNLOCKED广播的修改,对AOSP开机流程改动较大,暂时尚未有较好的优化方案,欢迎大神指教。