Direct Boot Mode

简介

在Android M及之前,当开机启动到锁屏界面时,所有程序阻塞,等待用户解锁(即使未设置开机密码,也需要滑屏解锁)后才会继续。

而Android 7.0引入了Direct Boot模式,当手机已经通电开机但是用户并有解锁锁屏的时候,Android N运行于一个安全的模式,也就是Dierect Boot模式。

而Direct Boot模式下,仅限于运行一些关键的、紧急的APP,比如:

  • Apps that have scheduled noti cations, such as alarm clock apps.
  • Apps that provide important user noti cations, like SMS apps.
  • Apps that provide accessibility services, like Talkback.

技术点

  1. 如何使APP支持Direct Boot?
    为了让APP应用能够在用户解锁设备之前运行,须在AndroidManinfest.xml清单文件中将组件显式标记为支持Direct Boot:
<activity|provider|receiver|service ...  android:directBootAware=”true”>
  1. 广播
    Android N在引入DBM后,也新增了一条ACTION_LOCKED_BOOT_COMPLETED广播:

    • 在进入DRM后,APP会收到广播消息: Intent.ACTION_LOCKED_BOOT_COMPLETED // 未解锁
    • 在用户解锁后,APP会收到另一条广播: Intent.ACTION_BOOT_COMPLETED

    应用获取解锁的通知:

    • 监听广播:ACTION_USER_UNLOCKED
    • 监听广播:ACTION_BOOT_COMPLETED
  2. 在Direct Boot模式下,应用只能访问其他支持DirectBoot的应用和组件;如果依赖外部服务或Activity,需要妥善处理外部服务或Activity不可用的情形;默认情况下,Intent过滤器仅匹配当前状态(已解锁/未解锁)下可用的组件。

启动FallbackHome流程

在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。

    <application android:label="@string/settings_label"android:icon="@mipmap/ic_launcher_settings"............android:directBootAware="true"><!-- Triggered when user-selected home app isn't encryption aware --><activity android:name=".FallbackHome"android:excludeFromRecents="true"android:theme="@style/FallbackHome"><intent-filter android:priority="-1000"><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.HOME" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity>

所以,在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()方法:

  • 判断用户是否已解锁,如果未解锁就什么都不做,函数退出;
  • 如果已解锁,则去寻找Launcher,如果找到,FallbackHome就会调用finish()结束自己;如若找不到就延迟500ms再找。

注意两点:

  • 如何寻找Launcher?
  • 如何延迟500ms ?

如下是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() {@Overridepublic 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上启动!

ACTION_USER_UNLOCKED广播(EnableScreen流程)

在开机接近尾声,WindowManagerService会调用enableScreenIfNeededLocked()函数判断是否enable screen,通过Handler发生ENABLE_SCREEN消息到主线程。而在mH的handleMessage()中处理ENABLE_SCREEN消息时,会调用performEnableScreen()函数。

WindowManagerService.performEnableScreen()函数中判断是否要enable screen的两个因素:

  • checkWaitingForWindows()
    Don’t enable the screen until all existing windows have been drawn.
  • checkBootAnimationCompleteLocked()
    在设置service.bootanim.exit属性后等待开机动画结束;

插叙:关于checkBootAnimationCompleteLocked()的内容:

  • 检查BOOT_ANIMATION_SERVICE是否已结束,如果已结束,返回true;
  • 如果未结束,则向Handler发送一个200ms的延时消息CHECK_IF_BOOT_ANIMATION_FINISHED;在此消息的处理过程中,会调用checkBootAnimationCompleteLocked()检查开机动画是否已结束,如果已结束,则会再次调用performEnableScreen(),否则继续延时等待;总之就是每200ms检查一次;
  • 也就是说,performEnableScreen()函数是一直等待开机动画结束,才会继续下面的流程;

注:checkBootAnimationCompleteLocked()向我们展示了通过Handler机制polling轮询的方法,这比while循环的方式更有效率,能够改善用户响应能力。

WindowManagerService.performEnableScreen()还做了如下动作:

  • 结束开机动画(即设置service.bootanim.exit属性为1)
  • 通知SurfaceFlinger,系统已Boot,如下:
IBinder surfaceFlinger = ServiceManager.getService("SurfaceFlinger");
Parcel data = Parcel.obtain();
data.writeInterfaceToken("android.ui.ISurfaceComposer");
surfaceFlinger.transact(IBinder.FIRST_CALL_TRANSACTION, // BOOT_FINISHEDdata, null, 0);
data.recycle();
  • 设置mDisplayEnabled=true
  • 调用InputMonitor.setEventDispatchingLw() // Enable input dispatch
    很关键,解释了为什么副屏APP界面早早起来了但却触摸无响应!
  • 调用ActivityManagerService.bootAnimationComplete()
    它会继续调用ActivityManagerService.finishBooting()
  • 调用PhoneWindowManager.enableScreenAfterBoot()
  • 调用updateRotationUnchecked() // 疑问:如何强制横屏显示?

继续:ActivityManagerService.bootAnimationComplete()函数

这里的关键是AMS的finishBooting()函数,它做了啥:

  • 关闭一些APP // ??
  • Enable Net Opts
  • 调用SystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED)函数;
    • 会对所有注册到ServiceManager的system service,调用其onBootPhase()函数;
  • 调用UserController.sendBootCompletedLocked()函数;

UserController类负责管理用户状态,如下:

  • finishUserBoot() // STATE_BOOTING --> STATE_RUNNING_LOCKED
  • finishUserUnlocking() // STATE_RUNNING_LOCKED --> STATE_RUNNING_UNLOCKING
  • finishUserUnlocked() // STATE_RUNNING_UNLOCKING --> STATE_RUNNING_UNLOCKED
  • stopSingleUserLocked() // STATE_RUNNING_XX --> STATE_STOPPING
  • finishUserStopping() // STATE_STOPPING --> STATE_SHUTDOWN

上述函数是依次被调用的关系:

  • UserController.sendBootCompletedLocked()调用finishUserBoot()
  • finishUserBoot()会调用maybeUnlockUser(),进而调用到finishUserUnlocking()
  • finishUserUnlocking()会向mHandler发送SYSTEM_USER_UNLOCK_MSG消息;
  • ActivityManagerService负责处理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广播;

AMS与UserController的关系大致如下图所示:

OK,我们再回头看下FallbackHome,当收到ACTION_USER_UNLOCKED广播,它就会调用maybeFinish()方法去启动Launcher!

注意:UserController不是一个独立模块,它是ActivityManager的一部分!

疑问:怎么没看到用户解锁的处理过程?

扩展:使能InputDispatcher

上面提到的InputMonitor.setEventDispatchingLw()会通过InputManagerService.setInputDispatchMode()最终调用Native层的InputDispatcher::setInputDispatchMode()函数。

Native层的InputDispatcher::setInputDispatchMode()函数,它就设置两个flag变量:

  • mDispatchEnabled
  • mDispatchFrozen

可以在shell里通过dumpsys input | grep DispatchEnabled命令来查看此变量的值。

InputDispatcher::dispatchOnceInnerLocked()函数分发Input事件时,会判断mDispatchEnabled和mDispatchFrozen这俩变量的值:

  • 若mDispatchEnabled为true,则丢弃此次Input事件(dropReason 为 DROP_REASON_DISABLED);
  • 若mDispatchFrozen为true,则直接return;

后记:如何优化启动速度

引入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开机流程改动较大,暂时尚未有较好的优化方案,欢迎大神指教。

参考链接

  • Support Direct Boot mode
  • Android 7.0: What is Direct Boot, and how will it improve your experience?
  • Android7.0 DirectBoot阻塞开机分析
  • Android N新特性 : Direct Boot Mode
  • Android Launcher 启动 Activity 的工作过程

Direct Boot Mode相关推荐

  1. Android 7.0 ----- Direct Boot模式(AppClock)

    本文针对闹钟应用对于此次Android 7.0增加DB模式所需要的应对方式. Direct Boot模式:设备已开机但用户尚未解锁设备时,Android 7.0 将在安全的"直接启动&quo ...

  2. Anroid 7.0 Direct Boot研究

    为了支持Dierect Boot模式,系统提供了两个存储数据的地方: 1.Credential encrypted storage,默认存储数据的地方,仅在用户解锁手机后可用. 2.Device en ...

  3. SystemUI 状态栏增加移动数据开启图标

    原生设计中,移动数据图标只有在网络活动(下载/上传)时,才显示相应的小白色三角图标,如果没有网络活动则没有任何显示.需要在不活动时也显示灰色的三角图标. 系统导航栏中常见信号图标包括:SIM卡信号(移 ...

  4. Android10 SystemUI状态栏网络图标流程分析

    Android 10 SystemUI网络图标刷新与显示 涉及文件目录: android/frameworks/base/packages/SystemUI/src/com/android/syste ...

  5. Android DirectBoot模式

    DirectBoot(简称DB)是Android N新引入的一个特性,本质上是对数据访问做了限制.在用户开机但未解锁之前,应用只能访问这个安全区内的数据,从而保护用户隐私安全. Android N上把 ...

  6. 【IOS开发进阶系列】动画专题

    1 CALayer IOS SDK详解之CALayer(一) http://doc.okbase.net/Hello_Hwc/archive/123447.html 1.1 基本概念 1.1.1 CA ...

  7. 207最新android书籍,《最强Android书 架构大剖析》PDF(高清版)

    目录 关于本书 XIV 第1 章 Android 体系结构的变革之路 1 1.1 Android 系统版本的历史变迁 2 Froyo(冻酸奶) 3 Gingerbread(姜饼人) 3 Honeyco ...

  8. Android--BroadcastReceiver

    1.概念 广播分为发广播和广播接收者,类似于广播发射器和收音机. 在整个Android开发中,主要研究的就是发广播和接收广播. 2.广播的分类 从发广播的角度来说:分为系统广播和第三方广播 从接收的顺 ...

  9. Android 加密之文件加密(FBE)

    一:加密简介: 加密是使用对称加密密钥对 Android 设备上的所有用户数据进行编码的过程.设备经过加密后,所有由用户创建的数据在存入磁盘之前都会自动加密,并且所有读取操作都会在将数据返回给调用进程 ...

最新文章

  1. Powershell管理系列(八)Exchange 2013通讯组管理
  2. 怎么用MYSQL分析财务数据_如何使用Mysql正确的处理财务数据
  3. CTFshow 命令执行 web73
  4. Hibernate框架--学习笔记(下):hibernate的查询方式、多表查询、检索策略、批量抓取
  5. maya扇子动画_maya怎么制作一个万箭齐发的效果?
  6. 程序员愤怒了:我们不仅要当管理者还要当CEO!
  7. python读取文件乱码
  8. ThinkPHP5 ajax传值到后台进行处理(验证是否含有某段字符串、截取字符串、字符串转数组)...
  9. 关于Request.params的知识收藏
  10. Ubuntu16.04安装MATLAB2014a
  11. 智能ai电话机器人源码加密开源版开发供应商
  12. SEO常用辅助工具整合
  13. SpringBoot从入门到精通二(SpringBoot整合myBatis的两种方式)
  14. fusioncharts java_FusionCharts在Java中的基本使用(2)
  15. 医院在线预约挂号系统开源
  16. 05月11日三支有望飙涨股与操盘策略分析
  17. 58java面试题_58同城java后台开发面试经验|面试题 - 职朋职业圈
  18. Google-Hacking语法总结
  19. echarts 地图行政区划压缩_echarts地图数据的压缩
  20. mysql count star_精品_MySQL常用SQL.md

热门文章

  1. 联想小新潮7000黑苹果教程_联想小新 潮7000-13怎么装win10系统|联想小新 潮7000-13用u盘重装win10系统教程-系统城...
  2. Oracle用OEM和命令行方式创建用户及表空间
  3. 虚幻引擎之方向轴制作总结
  4. 【高等数学】伯努利方程及其求解方法
  5. Hadoop面试连环炮
  6. Linux无界面创建Oracle数据库实例
  7. 算子法二阶线性非齐次微分方程的通解
  8. 数据采集的基本方法?
  9. STM32F407ZG 实现DMA收发数据
  10. fn映射 mac 键盘_Mac 与PC键盘的对比及快捷键(黑苹果)