系列导航 :一:全景概览与架构设计 | 二:启动流程深度解析 | 三:StatusBar 状态栏 | 四:NavigationBar 导航栏 | 五:通知系统 | 六:QuickSettings | 七:Keyguard 锁屏(本文) | 八:Recents 与其他子系统
一、概述
Keyguard(锁屏)是 Android 设备安全的第一道防线,也是用户开机后看到的第一个可见界面。它负责在设备锁定时阻止未授权访问,同时提供通知预览、紧急呼叫、相机快捷入口等功能。
在 AOSP7 中,Keyguard 不仅是一个独立的锁屏界面,还需要与 StatusBar、通知面板、导航栏紧密协作,涉及复杂的调度逻辑。
Keyguard 模块的代码分散在两个 package 里:packages/SystemUI 放调度器和视图,packages/Keyguard(包名 com.android.keyguard)放状态监听和安全模型。
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/
└── KeyguardViewMediator.java # ★ 锁屏调度核心(约 3000+ 行)
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/
├── StatusBarKeyguardViewManager.java # 锁屏与状态栏协调
├── KeyguardBouncer.java # 安全锁屏认证视图
└── KeyguardBottomAreaView.java # 锁屏底部区域(相机、紧急呼叫)
frameworks/base/packages/Keyguard/src/com/android/keyguard/ # 包名 com.android.keyguard
├── KeyguardUpdateMonitor.java # ★ 锁屏状态监听(SIM 卡、屏幕等)
└── KeyguardSecurityModel.java # 锁屏安全类型(SecurityMode)
关键设计 :
KeyguardUpdateMonitor和KeyguardSecurityModel都在packages/Keyguard下、包名是com.android.keyguard,不在SystemUI里------它们是共享给 Keyguard 独立进程用的状态/模型层。
二、Keyguard 整体架构
┌─────────────────────────────────────────────────────────┐
│ KeyguardViewMediator │
│ (调度核心) │
│ ┌─────────────────────────────────────────────────┐ │
│ │ 负责协调所有锁屏相关组件 │ │
│ │ - 处理屏幕 on/off 事件 │ │
│ │ - 管理锁屏显示/隐藏状态 │ │
│ │ - 协调安全认证流程 │ │
│ │ - 与 PhoneWindowManager 通信 │ │
│ └─────────────────────────────────────────────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌────────────────┐ ┌──────────────────────┐ │
│ │ KeyguardUpdate │ │ StatusBarKeyguard │ │
│ │ Monitor │ │ ViewManager │ │
│ │ (状态监听) │ │ (锁屏视图管理) │ │
│ └────────────────┘ └──────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ KeyguardBouncer │ │
│ │ (PIN/图案/密码) │ │
│ └──────────────────┘ │
└─────────────────────────────────────────────────────────┘
三、KeyguardViewMediator------锁屏调度核心
3.1 启动流程
回顾系列第二篇,KeyguardViewMediator 在 config_systemUIServiceComponents 中注册,并在 SystemUIApplication 中通过反射加载。start() 里做的第一件事是在锁内调用 setupLocked() 把所有依赖准备好,然后把自身注册进组件表:
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
java
public class KeyguardViewMediator extends SystemUI {
private PowerManager mPM; // 电源管理(用于 wake lock)
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private KeyguardUpdateMonitor mUpdateMonitor; // 锁屏状态监听
private boolean mShowing; // 是否正在显示锁屏
private LockPatternUtils mLockPatternUtils; // 密码/图案校验
@Override
public void start() {
synchronized (this) {
setupLocked();
}
putComponent(KeyguardViewMediator.class, this);
}
private void setupLocked() {
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
mLockPatternUtils = new LockPatternUtils(mContext);
// ... 注册广播、KeyguardDisplayManager、加载锁屏音效 ...
mStatusBarKeyguardViewManager =
SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,
mViewMediatorCallback, mLockPatternUtils);
}
}
关键设计 :
mPM是PowerManager(拿 wake lock 用的),不是PhoneWindowManager。视图管理器通过SystemUIFactory.createStatusBarKeyguardViewManager()工厂创建,便于定制替换。
3.2 与 PhoneWindowManager 的衔接
7.1.2 里 PhoneWindowManager 持有的是一个 binder KeyguardServiceDelegate mKeyguardDelegate,SystemUI 侧的调度器并不直接塞给它。KeyguardViewMediator 把自己注册进组件表(上面 start() 里的 putComponent(KeyguardViewMediator.class, this)),WindowManager 那边通过服务接口回调进来,锁屏显示/隐藏的语义由 KeyguardViewMediator 内部状态驱动。
关键设计 :调度器与 WMS 之间是 binder 委托 + 组件注册,而不是早期版本那种
mPM.setKeyguardDelegate(this)的直接注入。
3.3 锁屏显示流程
显示锁屏走 handleShow(Bundle):先置 mShowing 状态,再交给 StatusBarKeyguardViewManager 真正去 show 视图:
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
java
public class KeyguardViewMediator extends SystemUI {
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
private boolean mShowing;
private final ArrayList<IKeyguardStateCallback> mKeyguardStateCallbacks = new ArrayList<>();
private void handleShow(Bundle options) {
synchronized (KeyguardViewMediator.this) {
setShowingLocked(true);
mStatusBarKeyguardViewManager.show(options); // 真正显示锁屏视图
resetKeyguardDonePendingLocked();
}
}
private void setShowingLocked(boolean showing) {
if (showing != mShowing) {
mShowing = showing;
// 通知所有 IKeyguardStateCallback.onShowingStateChanged(showing)
updateInputRestrictedLocked();
}
}
}
关键设计 :显示与否的语义状态(
mShowing)由setShowingLocked统一维护,并同步广播给mKeyguardStateCallbacks;真正画视图的是mStatusBarKeyguardViewManager.show(options)。
3.4 锁屏隐藏流程
解锁完成时 Keyguard 视图回调 keyguardDone(boolean),它把事件 post 到 mHandler,最终落到 handleKeyguardDone:
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
java
public class KeyguardViewMediator extends SystemUI {
private KeyguardUpdateMonitor mUpdateMonitor;
private Handler mHandler;
public void keyguardDone(boolean authenticated) {
Message msg = mHandler.obtainMessage(KEYGUARD_DONE, authenticated ? 1 : 0);
mHandler.sendMessage(msg);
}
private void handleKeyguardDone(boolean authenticated) {
if (authenticated) {
mUpdateMonitor.clearFailedUnlockAttempts(); // 认证成功,清零失败计数
}
handleHide(); // 走隐藏流程
}
}
关键设计 :
keyguardDone只是投递消息,真正的状态翻转和handleHide()都在 handler 的 UI 线程执行,保证锁屏状态变更线程安全。
四、锁屏类型
AOSP7 支持多种锁屏方式:
4.1 是否安全(isSecure)
判断"当前用户到底有没有设锁"是决定滑动解锁还是安全锁屏的关键。isSecure() 综合了三种来源:
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
java
public class KeyguardViewMediator extends SystemUI {
private LockPatternUtils mLockPatternUtils;
private boolean mIsSubsidyOrDeviceLocked; // 运营商补贴锁/设备锁
public boolean isSecure() {
return mLockPatternUtils.isSecure(KeyguardUpdateMonitor.getCurrentUser())
|| KeyguardUpdateMonitor.getInstance(mContext).isSimPinSecure()
|| mIsSubsidyOrDeviceLocked;
}
}
关键设计:安全与否不只是"用户设了密码",还包括 SIM PIN 未解锁和设备补贴锁(subsidy lock)三种情况,任一成立即视为安全锁屏。
4.2 安全锁屏(PIN / 图案 / 密码)
安全类型由 KeyguardSecurityModel.SecurityMode 枚举统一描述,getSecurityMode() 根据当前 SIM 状态和设备策略算出应该用哪种解锁界面:
源码路径 :frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardSecurityModel.java
java
public class KeyguardSecurityModel {
public enum SecurityMode {
Invalid, // 空状态
None, // 未启用安全
Pattern, // 图案
Password, // 字母数字密码
PIN, // 纯数字 PIN
SimPin, // SIM PIN
SimPuk, // SIM PUK
DeviceSubsidy // 补贴锁
}
private LockPatternUtils mLockPatternUtils;
SecurityMode getSecurityMode() {
// 依次判断 SimPuk → SimPin → DeviceSubsidy
final int security = mLockPatternUtils.getActivePasswordQuality(
KeyguardUpdateMonitor.getCurrentUser());
switch (security) {
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC_COMPLEX:
return SecurityMode.PIN;
case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:
case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:
case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:
case DevicePolicyManager.PASSWORD_QUALITY_MANAGED:
return SecurityMode.Password;
case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:
return SecurityMode.Pattern;
case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:
return SecurityMode.None;
default:
throw new IllegalStateException("Unknown security quality:" + security);
}
}
}
KeyguardBouncer 本身是个普通类(不是接口、也不是抽象工厂),它负责认证视图的宿主。show(boolean resetSecuritySelection) 只保证宿主视图存在并展示主安全屏,具体的 PIN/图案/密码输入控件是由 KeyguardSecurityModel(SecurityMode)+ KeyguardHostView 驱动的,而不是 KeyguardBouncer 上的字段:
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
java
public class KeyguardBouncer {
protected KeyguardHostView mKeyguardView; // 宿主视图(内部承载 PIN/图案/密码)
protected ViewGroup mRoot;
protected ViewGroup mContainer;
public KeyguardBouncer(Context context, ViewMediatorCallback callback,
LockPatternUtils lockPatternUtils, StatusBarWindowManager windowManager,
ViewGroup container) {
mLockPatternUtils = lockPatternUtils;
mContainer = container;
}
public void show(boolean resetSecuritySelection) {
mFalsingManager.onBouncerShown();
ensureView();
if (resetSecuritySelection) {
mKeyguardView.showPrimarySecurityScreen(); // 刷新主安全屏
}
// ... 触发上滑 dismiss 或展开 bouncer 动画 ...
}
}
关键设计 :
getSecurityMode()属于KeyguardSecurityModel,不在KeyguardUpdateMonitor上;KeyguardBouncer只是宿主管道,具体输入界面由SecurityMode+ 宿主视图决定。
4.3 认证流程
用户唤醒设备(电源键)
│
▼
KeyguardViewMediator.handleNotifyScreenTurnedOn()
│
▼
handleShow(options) → StatusBarKeyguardViewManager.show(options)
显示锁屏界面(含通知预览)
│
▼
用户上滑或输入 PIN/图案/密码
│
├── 无安全锁(SecurityMode.None)→ 滑动直接解锁
│
└── 安全锁(PIN/Pattern/Password)
│
▼
KeyguardBouncer.show(resetSecuritySelection)
│
▼
KeyguardSecurityModel.getSecurityMode() 决定输入界面
│
LockPatternUtils 校验
│
┌────┴────┐
正确 错误
│ │
▼ ▼
keyguardDone(true) 显示错误提示
│
▼
handleKeyguardDone → handleHide(),显示桌面
五、StatusBarKeyguardViewManager------锁屏视图管理
KeyguardViewMediator 只管调度语义,真正管锁屏视图的是 StatusBarKeyguardViewManager。它在状态栏视图创建时被 registerStatusBar() 注册进来:
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
java
public class StatusBarKeyguardViewManager implements RemoteInputController.Callback {
protected PhoneStatusBar mPhoneStatusBar;
private ViewGroup mContainer; // 宿主容器(NotificationPanelView 的一部分)
private StatusBarWindowManager mStatusBarWindowManager;
protected KeyguardBouncer mBouncer;
protected boolean mShowing;
public void registerStatusBar(PhoneStatusBar phoneStatusBar,
ViewGroup container, StatusBarWindowManager statusBarWindowManager,
ScrimController scrimController,
FingerprintUnlockController fingerprintUnlockController) {
mPhoneStatusBar = phoneStatusBar;
mContainer = container;
mStatusBarWindowManager = statusBarWindowManager;
mScrimController = scrimController;
mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
mViewMediatorCallback, mLockPatternUtils, mStatusBarWindowManager, container);
}
public void show(Bundle options) {
mShowing = true;
mStatusBarWindowManager.setKeyguardShowing(true); // 通知窗口层切到锁屏
mScrimController.abortKeyguardFadingOut();
reset(); // 复位并展示 bouncer/通知锁屏
}
}
关键设计 :锁屏不是独立窗口------bouncer 通过
createKeyguardBouncer()生成后 inflate 进mContainer(NotificationPanelView的子区域),KeyguardBottomAreaView也是NotificationPanelView的子视图。show(Bundle)先置mShowing,再经StatusBarWindowManager通知窗口层切换锁屏态。
六、KeyguardUpdateMonitor------状态监听
KeyguardUpdateMonitor 是全局单例,聚合了锁屏关心的各类系统状态:设备是否完成开机引导、失败解锁次数、SIM 卡数据、锁屏可见性、屏幕是否点亮等。注意它的源码路径在 packages/Keyguard(包名 com.android.keyguard),不在 SystemUI 里。
源码路径 :frameworks/base/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
java
public class KeyguardUpdateMonitor implements TrustManager.TrustListener {
private static KeyguardUpdateMonitor sInstance;
private boolean mDeviceProvisioned; // 设备是否完成开机引导
private SparseIntArray mFailedAttempts = new SparseIntArray(); // 各用户失败解锁次数
private HashMap<Integer, SimData> mSimDatas; // 每个 subId 的 SIM 数据
private boolean mKeyguardIsVisible; // 锁屏是否可见
private boolean mScreenOn; // 屏幕是否点亮
public static KeyguardUpdateMonitor getInstance(Context context) {
if (sInstance == null) {
sInstance = new KeyguardUpdateMonitor(context);
}
return sInstance;
}
public void registerCallback(KeyguardUpdateMonitorCallback callback) {
// 以弱引用方式加入 mCallbacks,状态变化时回调通知
}
public boolean isSimPinSecure() {
// 依据 mSimDatas 判断是否要求 SIM PIN 解锁
}
public int getFailedUnlockAttempts(int userId) {
return mFailedAttempts.get(userId, 0);
}
public void clearFailedUnlockAttempts() {
mFailedAttempts.delete(sCurrentUser);
}
}
关键设计 :
KeyguardUpdateMonitor只负责状态聚合与回调分发,getInstance()是全局单例;它没有isSecure()/getSecurityMode()(安全类型判断在KeyguardSecurityModel,见 4.2),状态变更通过registerCallback(KeyguardUpdateMonitorCallback)弱引用订阅。
七、锁屏通知
锁屏状态下,通知可以显示在锁屏界面上,但每条通知按自身的 visibility 字段决定显示程度。这个字段是 Notification 上的真实常量:
| 通知敏感度 | 锁屏行为 |
|---|---|
VISIBILITY_PUBLIC |
完整显示通知内容 |
VISIBILITY_PRIVATE |
仅显示"内容已隐藏"提示 |
VISIBILITY_SECRET |
完全不显示 |
关键设计 :锁屏可见性由
Notification.visibility决定,VISIBILITY_PUBLIC才能展示完整内容,PRIVATE只露一条遮罩提示,SECRET则在锁屏上彻底不出现------这条策略与是否锁屏无关,取决于应用声明的可见级别。
八、锁屏底部区域
锁屏底部(KeyguardBottomAreaView)提供紧急呼叫和相机快捷入口。它的初始化在 onFinishInflate() 里完成(不是 onCreate),两个按钮分别是 EmergencyButton 和 KeyguardAffordanceView,点击统一走 onClick:
源码路径 :frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBottomAreaView.java
java
public class KeyguardBottomAreaView extends FrameLayout implements View.OnClickListener,
UnlockMethodCache.OnUnlockMethodChangedListener,
AccessibilityController.AccessibilityStateChangedCallback, View.OnLongClickListener {
private static final Intent SECURE_CAMERA_INTENT =
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA_SECURE)
.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
public static final Intent INSECURE_CAMERA_INTENT =
new Intent(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
private static final Intent PHONE_INTENT = new Intent(Intent.ACTION_DIAL);
private EmergencyButton mEmergencyButton; // 紧急呼叫
private KeyguardAffordanceView mCameraImageView; // 相机快捷入口
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mEmergencyButton = (EmergencyButton) findViewById(R.id.emergency_call_button);
mCameraImageView = (KeyguardAffordanceView) findViewById(R.id.camera_button);
// ...
mCameraImageView.setOnClickListener(this);
}
}
相机用 INSECURE_CAMERA_INTENT(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA),锁定时走 SECURE_CAMERA_INTENT;紧急呼叫则由 EmergencyButton 内部处理,其拨号 action 是 com.android.phone.EmergencyDialer.DIAL,而不是 Intent.ACTION_DIAL_EMERGENCY。
关键设计 :底部区域用
onFinishInflate()而非onCreate初始化;紧急呼叫按钮是EmergencyButton(自带com.android.phone.EmergencyDialer.DIAL拨号逻辑),相机按钮是KeyguardAffordanceView,二者都实现View.OnClickListener统一分发点击。
九、定制开发提示
9.1 移除锁屏
在 KeyguardViewMediator.setShowingLocked(boolean) 中把 mShowing 恒置 false,可让调度层认为锁屏永远不显示(注意这只是关闭锁屏显示,安全策略仍由 LockPatternUtils 管理)。
9.2 自定义锁屏壁纸
在 NotificationPanelView 的锁屏布局中添加自定义 ImageView。
9.3 修改锁屏通知显示策略
锁屏可见性由 Notification.visibility 决定(VISIBILITY_PUBLIC/PRIVATE/SECRET,见第七节);要强制显示需改通知过滤逻辑,而非简单覆盖一个 shouldShowOnKeyguard。
9.4 禁用相机快捷入口
在 KeyguardBottomAreaView 中把相机视图 mCameraImageView(KeyguardAffordanceView)设为 GONE 即可隐藏快捷入口。
9.5 常见踩坑点
- 锁屏无法显示 :检查
KeyguardViewMediator.start()是否正确putComponent注册 - 安全锁屏界面空白 :确认
KeyguardBouncerinflate 的宿主视图(R.layout.keyguard_bouncer)存在 - 解锁动画卡顿 :
keyguardDone()经 handler 切窗口,确保在 UI 线程执行 - 锁屏通知不显示 :检查通知的
visibility属性
十、小结
本文深入分析了 Keyguard 锁屏模块的完整实现:
- KeyguardViewMediator 是锁屏调度核心,管理锁屏显示/隐藏的完整生命周期
- KeyguardUpdateMonitor (在
packages/Keyguard)聚合 SIM 卡、屏幕、失败解锁次数等系统状态 - StatusBarKeyguardViewManager 管理锁屏视图------锁屏不是独立窗口,而是嵌入在
NotificationPanelView中 - KeyguardBouncer + KeyguardSecurityModel (
SecurityMode)处理安全锁屏认证(PIN/图案/密码) - KeyguardBottomAreaView 提供紧急呼叫和相机快捷入口
锁屏与状态栏的紧密协作是 SystemUI 中最复杂的交互场景之一。下一篇(本系列最后一篇)将分析 Recents 最近任务、VolumeUI、PowerUI 等子系统,并给出综合定制实战建议。
下一篇:AOSP7 SystemUI 源码解析(八):Recents 最近任务与其他子系统 + 定制实战