Android SystemUI组件(07)锁屏KeyguardViewMediator分析
该系列文章总纲链接:专题分纲目录 Android SystemUI组件
本章关键点总结 & 说明:
说明:本章节持续迭代之前章节的思维导图,主要关注左侧上方锁屏分析部分即可。
为了更好理解本文的内容,优先说明下SystemUI中与Keyguard(锁屏)相关的类,主要包括:
- KeyguardViewMediator:这是一个中介类,它负责管理锁屏的状态,包括锁屏的显示和隐藏,以及与锁屏相关的各种事件处理。它还负责在系统启动时初始化锁屏界面,并在设备准备好时启动锁屏。
- KeyguardService:这个类是锁屏服务的入口点,它接收来自系统其他部分的请求,如启动锁屏或结束锁屏动画。
- StatusBarKeyguardViewManager:负责管理状态栏中的锁屏视图,包括显示和隐藏锁屏界面。
- KeyguardUpdateMonitor:监控系统的各种状态变化,如电池状态、时间变化等,并在这些变化发生时更新锁屏界面。
- KeyguardBouncer:负责显示解锁界面,包括图案、PIN、密码等解锁方式。它处理用户的解锁尝试,并在成功或失败时提供反馈。
- KeyguardHostView:是锁屏界面的根视图,它承载了所有的解锁方式视图。
- KeyguardSecurityContainer:包含解锁方式的容器,如图案、PIN、密码等。
- KeyguardPatternView、KeyguardPINView、KeyguardPasswordView:这些类分别对应不同的解锁方式,它们实现了用户与锁屏界面的交互。
- KeyguardManager:系统服务,用于管理系统锁屏的状态,如启用或禁用锁屏。
这些类共同工作,提供了一个完整的锁屏体验,包括显示锁屏界面、处理用户解锁尝试、以及在设备准备好时启动锁屏。这些类构成了锁屏功能的核心。
接下来我们从KeyguardViewMediator.start开始分析,一直到KeyguardViewMediator的关键逻辑:doKeyguardLocked方法的内部实现,想了解为什么要从这里开始分析的,可以查看文章:Android SystemUI组件(01)SystemUIService启动。
1 KeyguardViewMediator start启动分析
锁屏KeyguardViewMediator是在systemUI中启动的,从KeyguardViewMediator.start()开始分析。对应代码实现如下:
public class KeyguardViewMediator extends SystemUI {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
private static final boolean DEBUG = KeyguardConstants.DEBUG;
private static final boolean DEBUG_SIM_STATES = KeyguardConstants.DEBUG_SIM_STATES;
private final static boolean DBG_WAKE = false;
private final static String TAG = "KeyguardViewMediator";
//...
@Override
public void start() {
synchronized (this) {
setupLocked();//初始化与锁屏相关的状态和组件
}
//将当前实例(this)注册到一个组件管理系统中,以便其他部分的代码可以访问和使用这个实例
putComponent(KeyguardViewMediator.class, this);
}
//...
}
其中关于putComponent
方法具体解释如下:
-
组件管理:
putComponent
方法通常是一个用于注册组件的工具方法。它将当前的KeyguardViewMediator
实例存储在一个全局或共享的组件容器中,使得其他类或模块可以通过这个容器来获取这个实例。 -
依赖注入:这种做法类似于依赖注入(Dependency Injection),允许系统中的其他部分在需要时获取
KeyguardViewMediator
的实例,而不需要直接创建它。这样可以减少类之间的耦合,提高代码的可维护性。 -
方便访问:通过将
KeyguardViewMediator
实例放入组件管理系统,其他类可以方便地访问锁屏的相关功能和状态。例如,其他类可能会调用getComponent(KeyguardViewMediator.class)
来获取当前的锁屏管理器实例,以便进行锁屏状态的检查或更新。
接下来我们详细分析代码setupLocked的实现,具体如下:
public class KeyguardViewMediator extends SystemUI {
//...
private void setupLocked() {
// 初始化系统服务和锁屏相关的WakeLock
mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWM = WindowManagerGlobal.getWindowManagerService();
mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
mShowKeyguardWakeLock.setReferenceCounted(false);
// 注册一个广播接收器,用于处理延迟锁屏的动作
mContext.registerReceiver(mBroadcastReceiver, new IntentFilter(DELAYED_KEYGUARD_ACTION));
// 初始化锁屏显示管理器和闹钟管理器服务
mKeyguardDisplayManager = new KeyguardDisplayManager(mContext);
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
// 获取锁屏更新监控实例和锁屏图案工具
mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
mLockPatternUtils = new LockPatternUtils(mContext);
mLockPatternUtils.setCurrentUser(ActivityManager.getCurrentUser());
// 根据设备设置和用户状态决定是否显示锁屏,并通知信任管理器锁屏显示状态已改变
setShowingLocked(!shouldWaitForProvisioning() && !mLockPatternUtils.isLockScreenDisabled());
mTrustManager.reportKeyguardShowingChanged();
// 创建状态栏锁屏视图管理器,用于管理锁屏界面的显示和交互
mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager(mContext,
mViewMediatorCallback, mLockPatternUtils);
// 获取内容解析器,用于访问系统设置
final ContentResolver cr = mContext.getContentResolver();
// 检查屏幕是否已经开启
mScreenOn = mPM.isScreenOn();
// 加载锁屏声音等
mLockSounds = new SoundPool(1, AudioManager.STREAM_SYSTEM, 0);
String soundPath = Settings.Global.getString(cr, Settings.Global.LOCK_SOUND);
if (soundPath != null) {
mLockSoundId = mLockSounds.load(soundPath, 1);
}
if (soundPath == null || mLockSoundId == 0) {
Log.w(TAG, "failed to load lock sound from " + soundPath);
}
soundPath = Settings.Global.getString(cr, Settings.Global.UNLOCK_SOUND);
if (soundPath != null) {
mUnlockSoundId = mLockSounds.load(soundPath, 1);
}
if (soundPath == null || mUnlockSoundId == 0) {
Log.w(TAG, "failed to load unlock sound from " + soundPath);
}
soundPath = Settings.Global.getString(cr, Settings.Global.TRUSTED_SOUND);
if (soundPath != null) {
mTrustedSoundId = mLockSounds.load(soundPath, 1);
}
if (soundPath == null || mTrustedSoundId == 0) {
Log.w(TAG, "failed to load trusted sound from " + soundPath);
}
int lockSoundDefaultAttenuation = mContext.getResources().getInteger(
com.android.internal.R.integer.config_lockSoundVolumeDb);
mLockSoundVolume = (float)Math.pow(10, (float)lockSoundDefaultAttenuation/20);
// 加载锁屏隐藏动画资源
mHideAnimation = AnimationUtils.loadAnimation(mContext,
com.android.internal.R.anim.lock_screen_behind_enter);
}
//...
}
setupLocked()
方法的主要目的是为KeyguardViewMediator
的正常运行做好准备,确保所有必要的组件和状态都已初始化,以便在设备锁屏和解锁时能够正确响应用户的操作和系统事件。具体来说,关键部分主要包括:
-
初始化系统服务:该方法会获取系统服务,如
PowerManager
、WindowManager
和TrustManager
,这些服务在锁屏和解锁过程中是必不可少的。 -
创建锁屏状态监控:它会实例化
KeyguardUpdateMonitor
,用于监控与锁屏相关的各种状态变化,如电池状态、SIM卡状态等。 -
设置锁屏和解锁声音:
setupLocked()
还会加载锁屏和解锁时的声音设置,以便在用户进行解锁操作时提供反馈。 -
注册广播接收器:该方法会注册一些广播接收器,以便监听特定的系统事件,如延迟锁屏等。
-
创建视图管理器:通过
SystemUIFactory
创建StatusBarKeyguardViewManager
,用于管理锁屏界面的显示和隐藏。
2 开机锁屏流程分析
当设备启动或用户按下电源键时,系统均显示锁屏界面。这个过程涉及KeyguardViewMediator类,它负责协调锁屏的显示和隐藏。接下来分2个部分来解读,一个是从系统的SystemServer启动到KeyguardViewMediator.onSystemReady的过程解读,另一个是SystemServer的SystemReady执行后KeyguardViewMediator具体做了哪些事情。
2.1 从SystemServer启动到KeyguardViewMediator的onSystemReady方法执行
系统SystemServer启动后执行startCoreServices,这里会执行WindowManagerService的systemReady方法,代码实现如下:
public final class SystemServer {
private static final String TAG = "SystemServer";
//...
private void startCoreServices() {
WindowManagerService wm = null;
//...
try {
wm.systemReady();
} catch (Throwable e) {
reportWtf("making Window Manager Service ready", e);
}
//...
}
//...
}
这里调用了WindowManagerService的systemReady方法,代码具体实现如下:
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
static final String TAG = "WindowManager";
//这里实际上mPolicy就是PhoneWindowManager对象
final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
//...
public void systemReady() {
mPolicy.systemReady();
}
//...
}
这里实际上就是调用了PhoneWindowManager的systemReady方法,代码具体实现如下:
public class PhoneWindowManager implements WindowManagerPolicy {
static final String TAG = "WindowManager";
static final boolean DEBUG = false;
//...
@Override
public void systemReady() {
mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
mKeyguardDelegate.onSystemReady();
readCameraLensCoverState();
updateUiMode();
synchronized (mLock) {
updateOrientationListenerLp();
mSystemReady = true;
mHandler.post(new Runnable() {
@Override
public void run() {
updateSettings();
}
});
}
}
//...
}
这里调用了mKeyguardDelegate的onSystemReady方法,代码具体实现如下:
public class KeyguardServiceDelegate {
public static final String KEYGUARD_PACKAGE = "com.android.systemui";
protected KeyguardServiceWrapper mKeyguardService;
//...
public void onSystemReady() {
if (mKeyguardService != null) {
mKeyguardService.onSystemReady();
} else {
mKeyguardState.systemIsReady = true;
}
}
//...
}
这里调用了KeyguardServiceWrapper的onSystemReady方法,代码具体实现如下:
public class KeyguardServiceWrapper implements IKeyguardService {
private KeyguardStateMonitor mKeyguardStateMonitor;
private IKeyguardService mService;
private String TAG = "KeyguardServiceWrapper";
//...
@Override // Binder interface
public void onSystemReady() {
try {
mService.onSystemReady();
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
}
//...
}
这里调用了KeyguardService的onSystemReady方法,代码具体实现如下:
public class KeyguardService extends Service {
static final String TAG = "KeyguardService";
static final String PERMISSION = android.Manifest.permission.CONTROL_KEYGUARD;
//...
private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
@Override // Binder interface
public void addStateMonitorCallback(IKeyguardStateCallback callback) {
checkPermission();
mKeyguardViewMediator.addStateMonitorCallback(callback);
}
//...
@Override // Binder interface
public void onSystemReady() {
checkPermission();
mKeyguardViewMediator.onSystemReady();
}
//...
}
}
这里调用了KeyguardViewMediator的onSystemReady方法,代码具体实现如下:
public class KeyguardViewMediator extends SystemUI {
// 默认的锁屏显示超时延迟时间(30秒)
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
// 锁屏完成挂起的超时时间(3秒)
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
//...
public void onSystemReady() {
mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE);
synchronized (this) {
mSystemReady = true;
// 注册更新监控的回调
mUpdateMonitor.registerCallback(mUpdateCallback);
// 检查是否使用弱生物识别(如指纹)且相关硬件已安装
if (mLockPatternUtils.usingBiometricWeak()
&& mLockPatternUtils.isBiometricWeakInstalled()) {
// 如果使用弱生物识别,则禁用备用解锁方式
mUpdateMonitor.setAlternateUnlockEnabled(false);
} else {// 否则,启用备用解锁方式
mUpdateMonitor.setAlternateUnlockEnabled(true);
}
// 执行锁屏逻辑
doKeyguardLocked(null);
}
// 可能发送用户已出现的广播
maybeSendUserPresentBroadcast();
}
//...
}
至此,执行到KeyguardViewMediator.onSystemReady的方法中。这里最关键的便是锁屏逻辑对应的doKeyguardLocked方法。接下来继续分析关键逻辑doKeyguardLocked方法。
2.2 KeyguardViewMediator关键逻辑:doKeyguardLocked方法
doKeyguardLocked的代码分析具体如下所示:
public class KeyguardViewMediator extends SystemUI {
private static final int KEYGUARD_DISPLAY_TIMEOUT_DELAY_DEFAULT = 30000;
private static final long KEYGUARD_DONE_PENDING_TIMEOUT_MS = 3000;
//...
private void doKeyguardLocked(Bundle options) {
// 检查是否允许外部控制锁屏的显示
if (!mExternallyEnabled) {
return;
}
// 如果锁屏已经显示,重置状态并返回
if (mStatusBarKeyguardViewManager.isShowing()) {
resetStateLocked();
return;
}
// 检查是否需要SIM卡,如果需要但SIM卡缺失或被禁用,则不显示锁屏
final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
final boolean absent = SubscriptionManager.isValidSubscriptionId(
mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.ABSENT));
final boolean disabled = SubscriptionManager.isValidSubscriptionId(
mUpdateMonitor.getNextSubIdForState(IccCardConstants.State.PERM_DISABLED));
final boolean lockedOrMissing = mUpdateMonitor.isSimPinSecure()
|| ((absent || disabled) && requireSim);
// 如果SIM卡锁定或缺失,并且设备还未完成初始设置向导,则不显示锁屏
if (!lockedOrMissing && shouldWaitForProvisioning()) {
return;
}
// 如果锁屏被禁用,并且SIM卡没有被锁定或缺失,则不显示锁屏
if (mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
return;
}
// 如果设备设置了Vold密码,并且当前用户需要输入Vold密码,则不显示锁屏
if (mLockPatternUtils.checkVoldPassword()) {
setShowingLocked(false);
hideLocked();
return;
}
// 如果所有条件都满足,则显示锁屏
showLocked(options);
}
//...
//...handler 发送消息 SHOW/HIDE
private void showLocked(Bundle options) {
if (DEBUG) Log.d(TAG, "showLocked");
// ensure we stay awake until we are finished displaying the keyguard
mShowKeyguardWakeLock.acquire();
Message msg = mHandler.obtainMessage(SHOW, options);
mHandler.sendMessage(msg);
}
//...handler处理
private Handler mHandler = new Handler(Looper.myLooper(), null, true /*async*/) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW:
handleShow((Bundle) msg.obj);
break;
case HIDE:
handleHide();
break;
//...
}
}
};
//handler关键处理方法handleShow
private void handleShow(Bundle options) {
synchronized (KeyguardViewMediator.this) {
// 如果系统还没有准备好,忽略显示锁屏的请求
if (!mSystemReady) {
return;
}
// 标记锁屏为显示状态
setShowingLocked(true);
// 调用状态栏锁屏视图管理器显示锁屏
mStatusBarKeyguardViewManager.show(options);
// 标记锁屏不是在隐藏状态
mHiding = false;
// 重置锁屏完成挂起的状态
resetKeyguardDonePendingLocked();
// 标记没有运行隐藏动画
mHideAnimationRun = false;
// 更新活动锁屏状态
updateActivityLockScreenState();
// 调整状态栏
adjustStatusBarLocked();
// 用户活动事件
userActivity();
// 在最后执行,以免延迟锁屏显示
playSounds(true);
// 释放锁屏显示时持有的WakeLock
mShowKeyguardWakeLock.release();
}
// 显示锁屏管理器
mKeyguardDisplayManager.show();
}
这里调用了mStatusBarKeyguardViewManager的show方法,代码具体实现如下:
public class StatusBarKeyguardViewManager {
//step1 显示锁屏
public void show(Bundle options) {
mShowing = true; // 标记锁屏为显示状态
mStatusBarWindowManager.setKeyguardShowing(true); // 通知状态栏窗口管理器锁屏正在显示
reset(); // 调用reset方法来重置锁屏状态
}
//step2 重置锁屏状态
public void reset() {
if (mShowing) {
if (mOccluded) {
mPhoneStatusBar.hideKeyguard(); // 如果锁屏被遮挡,隐藏锁屏
mBouncer.hide(false /* destroyView */); // 隐藏解锁界面(Bouncer)
} else {
showBouncerOrKeyguard(); // 显示解锁界面或锁屏
}
updateStates(); // 更新锁屏状态
}
}
//step3 显示解锁界面或锁屏
private void showBouncerOrKeyguard() {
if (mBouncer.needsFullscreenBouncer()) {
mPhoneStatusBar.hideKeyguard(); // 需要全屏解锁界面时,隐藏锁屏
mBouncer.show(true); // 显示解锁界面(Bouncer)
} else {
mPhoneStatusBar.showKeyguard(); // 不需要全屏解锁界面时,显示锁屏
mBouncer.hide(false); // 隐藏解锁界面(Bouncer)
mBouncer.prepare(); // 准备解锁界面(Bouncer)
}
}
}
接下来主要解读mBouncer.show和hide的实现及相关流程。主要以show方法(加载视图)为主。接下来继续分析KeyguardBouncer的show方法和hide方法流程,代码具体实现如下:
public class KeyguardBouncer {
private Context mContext;
//...
private final Runnable mShowRunnable = new Runnable() {
@Override
public void run() {
// 设置锁屏视图的可见性为可见
mRoot.setVisibility(View.VISIBLE);
// 恢复锁屏视图的活动状态
mKeyguardView.onResume();
// 开始锁屏视图的显示动画
mKeyguardView.startAppearAnimation();
// 清除锁屏即将显示的标志
mShowingSoon = false;
}
};
//...
//加载及锁屏界面关键流程
//step1 显示锁屏界面
public void show() {
// 确保锁屏视图已经创建,锁屏View的加载
ensureView();
// 如果锁屏视图已经是可见的或者即将显示,则不需要再次显示
if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
// 更新当前的安全方法,这在锁屏已经显示但当前安全方法发生变化时是必要的
mKeyguardView.show();
return;
}
// 尝试dismiss锁屏。如果没有设置安全模式,这将dismiss整个锁屏。
// 如果需要认证,则显示解锁界面(Bouncer)。
if (!mKeyguardView.dismiss()) {
// 设置标志,表示锁屏即将显示
mShowingSoon = true;
// 在多个帧上分散工作
mChoreographer.postCallbackDelayed(Choreographer.CALLBACK_ANIMATION, mShowRunnable,
null, 48);
}
}
//step2 确保锁屏视图已经创建
private void ensureView() {
//检查mRoot(锁屏界面的根视图)是否已经存在。
if (mRoot == null) {
//加载锁屏界面
inflateView();
}
}
//step3 加载锁屏界面
private void inflateView() {
// 如果之前已经添加过锁屏视图,先将其移除
removeView();
// 通过LayoutInflater从keyguard_bouncer布局文件中加载锁屏界面布局
mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null);
// 从加载的布局中获取KeyguardViewBase实例
mKeyguardView = (KeyguardViewBase) mRoot.findViewById(R.id.keyguard_host_view);
// 为锁屏视图设置锁图案工具,用于处理锁屏图案相关逻辑
mKeyguardView.setLockPatternUtils(mLockPatternUtils);
// 为锁屏视图设置ViewMediatorCallback,用于处理锁屏界面的回调事件
mKeyguardView.setViewMediatorCallback(mCallback);
// 将锁屏视图添加到容器视图中,确保它在容器的最后面
mContainer.addView(mRoot, mContainer.getChildCount());
// 初始时将锁屏视图的可见性设置为不可见
mRoot.setVisibility(View.INVISIBLE);
// 设置系统UI可见性,禁用HOME按钮,这样用户在锁屏界面上不会看到HOME按钮
mRoot.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
}
//...
//隐藏锁屏界面关键流程
//step1 隐藏锁屏界面
public void hide(boolean destroyView) {
// 取消任何即将执行的显示锁屏的操作
cancelShowRunnable();
// 如果锁屏视图不为空,则进行清理
if (mKeyguardView != null) {
// 移除锁屏视图上的解散动作,即用户不再能通过这个视图解散锁屏
mKeyguardView.setOnDismissAction(null);
// 清理锁屏视图,这可能包括重置状态、停止动画等
mKeyguardView.cleanUp();
}
// 如果传入的参数destroyView为true,则完全移除锁屏视图
if (destroyView) {
removeView();
} else if (mRoot != null) {
// 如果不销毁视图,只是将其设置为不可见
mRoot.setVisibility(View.INVISIBLE);
}
}
//step2 锁屏界面不显示,取消线程
private void cancelShowRunnable() {
// 从Choreographer中移除之前安排的动画帧更新回调
mChoreographer.removeCallbacks(Choreographer.CALLBACK_ANIMATION, mShowRunnable, null);
// 将mShowingSoon标志设置为false,表示锁屏界面不再即将显示
mShowingSoon = false;
}
}
最后,总结下整个流程,具体如下:
KeyguardViewMediator
类中的doKeyguardLocked
方法是用来控制锁屏界面显示的核心逻辑。这个方法会根据设备的当前状态和设置来决定是否显示锁屏。以下是该方法的概要流程:
-
检查锁屏状态:首先,方法会检查锁屏是否已经被外部控制(例如通过
setKeyguardEnabled
方法)禁用。如果锁屏已被禁用,则不会显示锁屏。 -
检查锁屏是否已显示:如果锁屏已经显示在屏幕上,方法会重置锁屏状态并返回,不会重复显示锁屏。
-
检查SIM卡状态:方法会检查SIM卡是否缺失或被禁用,如果设备需要SIM卡且SIM卡状态不正常,则不会显示锁屏。
-
检查设备设置:如果设备还未完成初始设置向导,且SIM卡被锁定或缺失,则不会显示锁屏。
-
检查锁屏设置:如果锁屏被设置为禁用,或者设备设置了Vold密码,方法会隐藏锁屏并返回。
-
显示锁屏:如果所有条件都满足,方法会调用
showLocked
方法来显示锁屏界面。
StatusBarKeyguardViewManager
类中的show
方法负责实际显示锁屏界面。它首先将锁屏的显示状态设置为true
,然后调用reset
方法来重置锁屏状态。reset
方法会根据锁屏是否被遮挡来决定是显示解锁界面(Bouncer)还是锁屏界面。
KeyguardBouncer
类中的show
方法用于显示解锁界面(Bouncer)。如果需要全屏解锁界面,它会隐藏锁屏并显示解锁界面。否则,它会显示锁屏并隐藏解锁界面,并准备解锁界面以供用户输入。
KeyguardBouncer
类中的hide
方法用于隐藏解锁界面。它会取消任何即将执行的显示操作,并根据传入的参数决定是销毁视图还是仅仅将其设置为不可见。
这些方法共同工作,确保了锁屏界面能够在适当的时机显示或隐藏,同时提供了用户反馈和设备安全性。