前面几篇文章大体介绍了SystemUI的两个模块,StatusBar和QuickSetting,这篇文章开始分析Keyguard模块。java
对于锁屏呢,须要有个基本认知,它分为两类,一是滑动锁屏,一是安全锁屏。滑动锁屏是指经过手指滑动便可解锁的锁屏,安全锁屏是指密码锁,图案锁,PIN码锁等等。这两种锁屏是在不一样的地方建立的,不可一律而论,而本文只分析滑动锁屏。android
根据SystemUI之StatusBar建立可知,整个SystemUI视图是由super_status_bar.xml
建立的数据库
<!--根布局FrameLayout-->
<com.android.systemui.statusbar.phone.StatusBarWindowView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:sysui="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true">
<com.android.systemui.statusbar.BackDropView android:id="@+id/backdrop">
<ImageView android:id="@+id/backdrop_back" />
<ImageView android:id="@+id/backdrop_front"/>
</com.android.systemui.statusbar.BackDropView>
<com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_behind" />
<!--状态栏容器-->
<FrameLayout android:id="@+id/status_bar_container" />
<!--整个下拉通知面版,包括滑动锁屏界面-->
<include layout="@layout/status_bar_expanded" />
<!--这里亮度调节bar-->
<include layout="@layout/brightness_mirror" />
<com.android.systemui.statusbar.ScrimView android:id="@+id/scrim_in_front"/>
<!--在锁屏界面,显示在状态栏之下,显示在时间之上-->
<LinearLayout android:id="@+id/lock_icon_container">
<com.android.systemui.statusbar.phone.LockIcon android:id="@+id/lock_icon" />
<com.android.keyguard.KeyguardMessageArea android:id="@+id/keyguard_message_area" />
</LinearLayout>
</com.android.systemui.statusbar.phone.StatusBarWindowView>
复制代码
在这个布局中,ID为lock_icon_container
的布局是在锁屏界面上显示锁图标和一些信息的。这个布局include了一个status_bar_expanded.xml
布局,这是整个下拉通知面版,包括滑动锁屏的各类控件,来看看这个布局安全
<com.android.systemui.statusbar.phone.NotificationPanelView android:id="@+id/notification_panel" >
<FrameLayout android:id="@+id/big_clock_container" />
<!--滑动锁屏界面状态视图: 时间,日期-->
<include layout="@layout/keyguard_status_view" android:visibility="gone" />
<com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer android:id="@+id/notification_container_parent">
<include layout="@layout/dock_info_overlay" />
<!--QS界面-->
<FrameLayout android:id="@+id/qs_frame" android:layout="@layout/qs_panel" />
<!--显示通知的容器-->
<com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout android:id="@+id/notification_stack_scroller"/>
<include layout="@layout/ambient_indication" android:id="@+id/ambient_indication_container" />
<ViewStub android:id="@+id/keyguard_user_switcher" />
<!--滑动锁屏状态栏-->
<include layout="@layout/keyguard_status_bar" android:visibility="invisible" />
<Button android:id="@+id/report_rejected_touch" />
</com.android.systemui.statusbar.phone.NotificationsQuickSettingsContainer>
<!--这里锁屏底部图标, 填充整个父布局,例如左下角的图标,右下角的图标Camera-->
<include layout="@layout/keyguard_bottom_area" android:visibility="gone" />
<com.android.systemui.statusbar.AlphaOptimizedView android:id="@+id/qs_navbar_scrim"/>
</com.android.systemui.statusbar.phone.NotificationPanelView>
复制代码
能够看到,滑动锁屏的各个部分比较分散,并非在同一容器中集中建立的。app
通常咱们经过电源键的开关来锁屏的,本文来分析下,从开机到滑动锁屏显示的过程。ide
在开机的过程当中,当ActivityManagerService启动完毕后,会建立SystemUIoop
// frameworks/base/services/java/com/android/server/SystemServer.java
private static void startSystemUi(Context context, WindowManagerService windowManager) {
Intent intent = new Intent();
// 1. 启动 SystemUIService
intent.setComponent(new ComponentName("com.android.systemui",
"com.android.systemui.SystemUIService"));
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
context.startServiceAsUser(intent, UserHandle.SYSTEM);
// 2. 通知WMS,SystemUI已经启动
windowManager.onSystemUiStarted();
}
复制代码
前面的文章已经分析了SystemUI的启动,这个过程建立了整个SystemUI的视图,包括滑动锁屏的视图。如今来看看WindowManagerService在SystemUI启动后,作了什么布局
// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public void onSystemUiStarted() {
mPolicy.onSystemUiStarted();
}
复制代码
Window Manager 通知了 PhoneWindowManager , SystemUI 已经启动post
// frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
public void onSystemUiStarted() {
// 绑定 KeyguardService
bindKeyguard();
}
private void bindKeyguard() {
synchronized (mLock) {
if (mKeyguardBound) {
return;
}
mKeyguardBound = true;
}
// 绑定 KeyguardService 的操做交给了代理类 KeyguardServiceDelegate
mKeyguardDelegate.bindService(mContext);
}
复制代码
策略类 PhoneWindowManager 经过一个代理类 KeyguardServiceDelegate 来绑定了 KeyguardService。动画
KeyguardService 是一个标准的 Service,在绑定它的时候会返回一个 IBinder 对象,也就是服务端接口。咱们在后面直接称 KeyguardService 为锁屏服务端。
// frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
public void bindService(Context context) {
Intent intent = new Intent();
final Resources resources = context.getApplicationContext().getResources();
// KeyguardService
final ComponentName keyguardComponent = ComponentName.unflattenFromString(
resources.getString(com.android.internal.R.string.config_keyguardComponent));
intent.addFlags(Intent.FLAG_DEBUG_TRIAGED_MISSING);
intent.setComponent(keyguardComponent);
if (!context.bindServiceAsUser(intent, mKeyguardConnection,
Context.BIND_AUTO_CREATE, mHandler, UserHandle.SYSTEM)) {
// ...
}
}
复制代码
这就是一个标准的绑定 Service 流程,经过绑定时传入的参数 mKeyguardConnection 能够查当作功绑定后的操做
// frameworks/base/services/core/java/com/android/server/policy/keyguard/KeyguardServiceDelegate.java
private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mKeyguardService = new KeyguardServiceWrapper(mContext,
IKeyguardService.Stub.asInterface(service), mCallback);
if (mKeyguardState.systemIsReady) {
mKeyguardService.onSystemReady();
if (mKeyguardState.currentUser != UserHandle.USER_NULL) {
mKeyguardService.setCurrentUser(mKeyguardState.currentUser);
}
if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE
|| mKeyguardState.interactiveState == INTERACTIVE_STATE_WAKING) {
mKeyguardService.onStartedWakingUp();
}
if (mKeyguardState.interactiveState == INTERACTIVE_STATE_AWAKE) {
mKeyguardService.onFinishedWakingUp();
}
if (mKeyguardState.screenState == SCREEN_STATE_ON
|| mKeyguardState.screenState == SCREEN_STATE_TURNING_ON) {
mKeyguardService.onScreenTurningOn(
new KeyguardShowDelegate(mDrawnListenerWhenConnect));
}
if (mKeyguardState.screenState == SCREEN_STATE_ON) {
mKeyguardService.onScreenTurnedOn();
}
mDrawnListenerWhenConnect = null;
}
if (mKeyguardState.bootCompleted) {
mKeyguardService.onBootCompleted();
}
if (mKeyguardState.occluded) {
mKeyguardService.setOccluded(mKeyguardState.occluded, false /* animate */);
}
if (!mKeyguardState.enabled) {
mKeyguardService.setKeyguardEnabled(mKeyguardState.enabled);
}
}
}
复制代码
经过查询各类状态,而后按顺序向锁屏服务端发送指令。而我只分析服务端的onSystemReady()
的实现
代理类 KeyguardServiceDelegate 内部经过 KeyguardState 对象保存锁屏的状态,从而控制 KeyguardService 的行为。
// frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
public void onSystemReady() {
// SYSTEM 用户或者有 android.Manifest.permission.CONTROL_KEYGUARD 权限
checkPermission();
mKeyguardViewMediator.onSystemReady();
}
}
复制代码
锁屏服务端又展转通知了 KeyguardViewMediator,KeyguardViewMediator 向 Handler 发送了一个 SYSTEM_READY
事件来处理,最终会调用 handleSystemReady()
- KeyguardViewMediator 是典型的中介者模式的应用,它综合了各方面的信息来控制锁屏。
- KeyguardViewMediator 有一个与主线程 Looper 关联的 Handlder,事件和处理都是经过这个 Handler,所以保证了事件处理的有序性,这样就不会致使界面刷新混乱。
// frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
private void handleSystemReady() {
synchronized (this) {
if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true;
// 开启锁屏, 这里参数为 null
doKeyguardLocked(null);
// KeyguardUpdateMonitor 经过监听数据库Uri, 注册广播接收器,向各类服务注册监听,从而获取到与锁屏有关的更新
// KeyguardViewMediator 这个中介者关心的回调以下
// 1. onClockVisibilityChanged
// 2. onDeviceProvisioned
// 3. onSimStateChanged
// 4. onBiometricAuthFailed, onBiometricAuthenticated
// 5. onHasLockscreenWallpaperChanged
mUpdateMonitor.registerCallback(mUpdateCallback);
}
// Most services aren't available until the system reaches the ready state, so we
// send it here when the device first boots.
maybeSendUserPresentBroadcast();
}
复制代码
KeyguardMediator 经过 doKeyguardLocked()
来开启锁屏,这里也是经过主线程的 Handler 来处理的,最终调用 handleShow()
// frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
private void handleShow(Bundle options) {
// ...
synchronized (KeyguardViewMediator.this) {
if (!mSystemReady) {
return;
}
// 通知监听都,锁屏状态改变了
setShowingLocked(true, mAodShowing);
// 显示锁屏屏界面
mStatusBarKeyguardViewManager.show(options);
mHiding = false;
mWakeAndUnlocking = false;
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
// 经过StatusBarManager调整状态栏
adjustStatusBarLocked();
userActivity();
// KeyguardUpdateMonitor 保存锁屏状态
mUpdateMonitor.setKeyguardGoingAway(false /* away */);
mShowKeyguardWakeLock.release();
}
// 更新显示什么
mKeyguardDisplayManager.show();
}
复制代码
锁屏的中介者 KeyguardViewMediator 最终把显示锁屏这个任务交给了 StatusBarKeyguardViewManager。
StatusBarKeyguardViewManager 管理锁屏的建立,显示,隐藏,重置。
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
public void show(Bundle options) {
mShowing = true;
// 更新锁屏状态
mStatusBarWindowController.setKeyguardShowing(true);
// 通知各类关心锁屏状态的组件,锁屏状态改变了
mKeyguardMonitor.notifyKeyguardState(
mShowing, mKeyguardMonitor.isSecure(), mKeyguardMonitor.isOccluded());
// 重置锁屏界面。参数为true,就表示当显示滑动锁屏时,隐藏安全锁界面
reset(true /* hideBouncerWhenShowing */);
}
复制代码
KeyguardMonitorImpl 介绍
- 它是一个单例。
- 它接收注册回调。
- 它利用了 KeyguardUpdateMonitor 只监听了 onTrustedChanged() 回调,而且在事件发生时,会调用各类已经注册的回调。
- 它暴露了一些 public 方法,经过这些方法设置锁屏状态,而后经过已经注册的回调。
- 它的做用就是供 SystemUI 其它组件获取最新的锁屏状态。
锁屏管理者 StatusBarKeyguardViewManager 先通知各路人马,锁屏状态改变了,而后重置了锁屏界面
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
public void reset(boolean hideBouncerWhenShowing) {
if (mShowing) {
if (mOccluded && !mDozing) {
// ...
} else {
// 参数为true, 表示隐藏安全锁界面,显示滑动锁界面
showBouncerOrKeyguard(hideBouncerWhenShowing);
}
// 调用 KeyguardUpdateMonitor#handleKeyguardReset()
// 更新指纹解锁,面部解锁状态
KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset();
updateStates();
}
}
protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
/** * 注意: needsFullscreenBouncer() 会加载安全锁视图,并添加到SystemUI的根视图中 * 可是只有显示SIM PIN/PUK锁时,这个方法才会返回true, 表示须要全屏显示 */
if (mBouncer.needsFullscreenBouncer() && !mDozing) {
// ...
} else {
// 显示滑动锁屏
mStatusBar.showKeyguard();
// 隐藏安全锁
if (hideBouncerWhenShowing) {
hideBouncer(shouldDestroyViewOnReset() /* destroyView */);
mBouncer.prepare();
}
}
// 更新各方状态
updateStates();
}
复制代码
reset()
这个方法的名字起的不是很直观,它作了不少事,主要有
加载安全锁视图的过程就不分析了,很简单,就是加载布局,而后添加到 SystemUI 根视图中。
本该只关心滑动锁,从代码能够看到,锁屏管理者 StatusBarKeyguardViewManager 把显示滑动锁的任务交给了 StatusBar。
从SystemUI之StatusBar建立可知,StatusBar 建立并管理了整个 SystemUI 视图,所以最终由它来显示滑动锁是瓜熟蒂落的。
StatusBar#showKeyguard()
的调用链很长,这里就不展现代码了,直接给出调用链。
showKeyguard()
-> updateIsKeyguard()
-> showKeyguardImpl()
-> updatePanelExpansionForKeyguard()
-> instantExpandNotificationsPanel()
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
public void showKeyguard() {
// 保存状态
mStatusBarStateController.setKeyguardRequested(true);
mStatusBarStateController.setLeaveOpenOnKeyguardHide(false);
mPendingRemoteInputView = null;
// 更新滑动锁屏,最终调用了 instantExpandNotificationsPanel()
updateIsKeyguard();
mAssistManager.onLockscreenShown();
}
@Override
public void instantExpandNotificationsPanel() {
// Make our window larger and the panel expanded.
// 设置通知面版可见
makeExpandedVisible(true);
// 展开,参数为false表示不须要动画 
mNotificationPanel.expand(false /* animate */);
mCommandQueue.recomputeDisableFlags(mDisplayId, false /* animate */);
}
复制代码
NotificationPanelView mNotificationPanel
是整个下拉通知面版的父容器,在文章开头,经过查看布局能够发现,滑动锁屏的大部分控件都是在下拉通知面版里,所以这里的操做就不奇怪了。
// frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@Override
public void expand(boolean animate) {
// 经过父类PanelView实现展开操做,并最终调用 requestLayout() 从新布局
super.expand(animate);
// 让各类控件监听它感兴趣的事情,例如 KeyguardStatusBarView 监听电池事件
setListening(true);
}
复制代码
这里就属于Android控件的各类操做了,这里就不展开了,最终它会调用 requestLayout()
从新布局,所以它会调用 onLayout()
来更新布局
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
// ...
// 显示 Clock 和 Notification
positionClockAndNotifications();
// ...
// 这里更新了锁屏大的时钟,以及底部图标区域
updateExpandedHeight(getExpandedHeight());
// 显示时间,日期的状态视图
updateHeader();
// ...
}
复制代码
能够看到各类锁屏的控件显示出来了。
从系统启动到显示滑动锁屏,这个过程其实并不复杂,有了这个基础,就能够分析安全锁界面的显示过程了。
另外,咱们应该对电源键控制锁屏的显示与隐藏很感兴趣,其实本文也给出了实现,例如点亮屏幕显示锁屏的过程是
DisplayManagerService
-> DisplayPowerController
-> PhoneWindowManager#screenTurnedOff()
-> KeyguardServiceDelegate#onScreenTurnedOff()
-> KeyguardService#onScreenTurnedOff()
。
SystemUI 还有不少模块,下篇挑个简单点的吧,就分析 VolumeUI 吧,让咱们继续为 SystemUI 窒息。