研究了将近两天的Android5.1 Keyguard锁屏机制,不得不说,各种饶。这里先把锁屏流程时序图贡献给大家:
使用的是线编辑工具ProcessOn,用来编辑时序图效果看起来不是太好。不过没有太大关系,这个时序图只是为了方便我们能清晰的对锁屏流程有个大致的了解,接下来,我会详细的分析每个类的具体流程。
声明:本文基于AndroidLollipop 5.1.1_r6版本进行的源码分析。
准备先从开机启动到PhoneWindowManager类的systemReady方法调用开始介绍。开机启动流程其实也很复杂,但是本文重点在于Keyguard锁屏的展示,所以这里只是大体列出如何从开机启动调用到PhoneWindowManager的systemReady()方法。具体流程如下:
init进程->zygote进程(java世界)->system server进程。
而在system server进程中,它的SystemServer.java中main函数会调用startOtherServices()方法,相关源码如下:
private void startOtherServices() {
WindowManagerService wm = null;
wm = WindowManagerService.main(context, inputManager,
mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
!mFirstBoot, mOnlyCore);
try {
wm.systemReady();
} catch (Throwable e) {
reportWtf("makeing Window Manager Service ready", e);
}
}
既然wm是WindowManagerServer类的实例,那就需要继续看一下这个类关于systemReady()方法的实现:
public class WindowManagerService extends IWindowManager.Stub implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
final WindowManagerPolicy mPolicy = PolicyManager.makeNewWindowManager();
public void systemReady() {
mPolicy.systemReady();
}
}
其中,mPolicy实例的生成依赖于Java的反射机制,具体流程如下:
public final class PolicyManager {
private static final String POLICY_IMPL_CLASS_NAME =
"com.android.internal.policy.impl.Policy";
private static final IPolicy sPolicy;
static {
try {
// 获取了Policy的类类型
Class policyClass = Class.forName(POLICY_IMPL_CLASS_NAME);
// 通过Policy的类类型获取Policy的对象实例
sPolicy = (IPolicy)policyClass.newInstance();
} catch (Exception e) {
}
}
public static Window makeNewWindowManager(Context context) {
return sPolicy.makeNewWindowManager(context);
}
}
// Policy中继续调用反射机制进行预加载
public class Policy implements IPolicy {
public Window makeNewWindowManager(Context context) {
return new PhoneWindowManager();
}
}
通过上述代码的跟踪,终于来到了我们时序图的第一个类PhonewWindowManager的systemReady()方法了。
既然上述分析到了PhoneWindowManager,我们也就是按照时序图,根据时序图上的每个类,进行相关源码分析(重点难点的源码我会中文注释)。
路径
frameworks/base/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
源码:
public class PhoneWindowManager implements WindowManagerPolicy {
public void systemReady() {
// 调用Keygurad代理类的onSystemReady方法
mKeyguardDelegate = new KeyguardServiceDelegate(mContext);
mKeyguardDelegate.onSystemReady();
// ... 省略不相关源码
}
}
路径:
frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceDelegate.java
源码:
public class KeyguardServiceDelegate {
protected KeyguardServiceWrapper mKeyguardService;
public void onSystemReady() {
if (mKeyguardService != null) {
mKeyguardService.onSystemReady();
} else {
mKeyguardState.systemIsReady = true;
}
}
}
路径:
frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/KeyguardServiceWrapper.java
源码:
public class KeyguardServiceWrapper implements IKeyguardService {
private IkeyguardService mService;
public KeyguardServiceWrapper(Context context, IKeyguardService service) {
// 构造函数中对mService进行了初始化
mService = service;
}
public void onSystemReady() {
try {
mService.onSystemReady();
} catch (RemoteException e) {
Slog.w(TAG , "Remote Exception", e);
}
}
}
由于mService是IKeyguardService接口实现类的实例,并且mService又是在KeyguardServiceWrapper的构造函数中传递进来初始化的。所以,我们又需要回到KeyguardServiceDelegate类,去看一下KeyguardServiceWrapper初始化的过程,相关代码如下:
public class KeyguardServiceDelegate {
public static final String KEYGUARD_PACKAGE = "com.android.systemui";
public static final String KEYGUARD_CLASS = "com.android.systemui.keyguard.KeyguardService";
/** * 使用bindService的方式来绑定服务。利用bindService的方式: * 调用者与服务绑定在一起,调用者退出,服务即终止。 * ps => bind方式绑定服务,服务的执行顺序为: * onCreate()->onBind()->onUnbind()->onDestroy() */
public void bindService(Context context) {
Intent intent = new Intent();
intent.setClassName(KEYGUARD_PACKAGE, KEYGUARD_CLASS);
if (!context.bindServiceAsUser(intent, mKeyguardConnection,
Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {
Log.v(TAG, "*** Keyguard: can't bind to " + KEYGUARD_CLASS);
mKeyguardState.showing = false;
mKeyguardState.showingAndNotOccluded = false;
mKeyguardState.secure = false;
mKeyguardState.deviceHasKeyguard = false;
hideScrim();
} else {
if (DEBUG) Log.v(TAG, "*** Keyguard started");
}
}
private final ServiceConnection mKeyguardConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG) Log.v(TAG, "*** Keyguard connected (yay!)");
// 当和服务绑定后,这IKeyguardService.Stub.asInterface(service)获取的就是KeyguardService的类实例
mKeyguardService = new KeyguardServiceWrapper(mContext,
IKeyguardService.Stub.asInterface(service));
if (mKeyguardState.systemIsReady) {
// If the system is ready, it means keyguard crashed and restarted.
mKeyguardService.onSystemReady();
// This is used to hide the scrim once keyguard displays.
mKeyguardService.onScreenTurnedOn(new KeyguardShowDelegate(
mShowListenerWhenConnect));
mShowListenerWhenConnect = null;
}
if (mKeyguardState.bootCompleted) {
mKeyguardService.onBootCompleted();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)");
mKeyguardService = null;
}
};
}
接下来,就需要去看一下KeyguardService类的onSystemReady方法了。
路径:
/frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
源码:
public class KeyguardService extends Service {
private KeyguardViewMediator mKeyguardViewMediator;
@Override // Binder interface
public void onSystemReady() {
// 检查调用进程是否具体SYSTEM权限
checkPermission();
// 真正的锁屏入口
mKeyguardViewMediator.onSystemReady();
}
}
Wow,终于到了我们的主角KeyguardViewMediator登场了。
路径:
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
虽然KeyguardViewMediator是锁屏的入口,但是从这里到锁屏的真正展现还有很长一段路。接下来,为了方便,我们都是基于当前类的函数进行分析。
public class KeyguardViewMediator extends SystemUI {
// 开机显示锁屏入口函数
public void onSystemReady() {
synchronized (this) {
if (DEBUG) Log.d(TAG, "onSystemReady");
mSystemReady = true;
// 判断是否使用生物识别解锁(类似:人脸识别、声音识别等)
if (mLockPatternUtils.usingBiometricWeak()
&& mLockPatternUtils.isBiometricWeakInstalled()) {
if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");
mUpdateMonitor.setAlternateUnlockEnabled(false);
} else {
mUpdateMonitor.setAlternateUnlockEnabled(true);
}
// 进行锁屏预处理判断等操作
doKeyguardLocked(null);
}
}
}
其中,doKeyguardLocked是来做启动锁屏界面的预处理方法,我们来看一下这个函数的具体实现:
public class KeyguardViewMediator extends SystemUI {
private void doKeyguardLocked(Bundle options) {
if (!mExternallyEnabled) {
// 其他应用禁止锁屏呈现,例如接电话等操作.
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");
return;
}
// 判断锁屏是否正在展示
if (mStatusBarKeyguardViewManager.isShowing()) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");
resetStateLocked();
return;
}
// 判断是否无sim卡也可使用手机
final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim", false);
// 获取sim卡状态
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);
if (!lockedOrMissing && shouldWaitForProvisioning()) {
if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"
+ " and the sim is not locked or missing");
return;
}
if (mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {
// Settings中没有启用锁屏
return;
}
if (mLockPatternUtils.checkVoldPassword()) {
setShowingLocked(false);
hideLocked();
return;
}
// 经过上述判断后,去展示锁屏
showLocked(options);
}
}
注意showLocked(options)方法调用,这个是启动锁屏的关键方法。这里的options传递的值为null。
public class KeyguardViewMediator extends SystemUI {
private void showLocked(Bundle options) {
if (DEBUG) Log.d(TAG, "showLocked");
// 获取PARTIAL_WAKE_LOCK,不受电源键影响,不让CPU进入休眠状态
mShowKeyguardWakeLock.acquire();
// 发送msg.what为SHOW类型的message
Message msg = mHandler.obtainMessage(SHOW, options);
mHandler.sendMessage(msg);
}
}
注意:
mShowKeyguardWakeLock.acquire(); ⇒ 获取之后是无法让CPU休眠,不要忘记释放,不让会增加系统功耗。
跟mShowKeyguardWakeLock相关的代码如下:
public class KeyguardViewMediator extends SystemUI {
private PowerManager.WakeLock mShowKeyguardWakeLock;
private void setupLocked() {
// 获取了PARTIAL_WAKE_LOCK锁,即不受电源键控制,即使按下电源键也不能使系统进入休眠状态
mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
mShowKeyguardWakeLock.setReferenceCounted(false);
}
private void showLocked(Bundle options) {
// 获取PARTIAL_WAKE_LOCK
mShowKeyguardWakeLock.acquire();
}
private void handleShow(Bundle options) {
synchronized (KeyguardViewMediator.this) {
// 释放PARTIAL_WAKE_LOCK
mShowKeyguardWakeLock.release();
}
}
}
既然是Handler Message机制,那我们就要去看一下mHandler类实例是如何处理SHOW类型的消息了。mHandle处理SHOW类型消息的方法如下:
public class KeyguardViewMediator extends SystemUI {
private Handler mHandler = new Handler(Looper.myLooper(), null /*callback*/, true /*async*/) {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW:
handleShow((Bundle) msg.obj);
}
}
}
private void handleShow(Bundle options) {
synchronized (KeyguardViewMediator.this) {
if (!mSystemReady) {
// 系统未Ready,则不呈现锁屏
return;
} else {
if (DEBUG) Log.d(TAG, "handleShow");
}
setShowingLocked(true);
// 展示锁屏界面
mStatusBarKeyguardViewManager.show(options);
mHiding = false;
resetKeyguardDonePendingLocked();
mHideAnimationRun = false;
updateActivityLockScreenState();
adjustStatusBarLocked();
userActivity();
// Do this at the end to not slow down display of the keyguard.
playSounds(true);
mShowKeyguardWakeLock.release();
}
mKeyguardDisplayManager.show();
}
}
针对这个方法,有几个重点需要强调一下。
Android5.1和Android4.4锁屏机制展示的区别?
解答:在Android5.1中,keyguard本身不再是一个独立的apk,而是跟SystemUI进行了合并,作为SystemUI的静态库进行调用。对比Android5.1和Android4.4的SystemUI模块的Android.mk文件可以更加直观的对比。
Android5.1 SystemUI Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java--files-under, src)
# 指定Keyguard作为静态库
LOCAL_STATIC_JAVA_LIBRARIES := Keyguard
LOCAL_JAVA_LIBRATIES := telephony-common
# 指定名称
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
LOCAL_RESOURCE_DIR := \
frameworks/base/packages/Keyguard/res \
$(LOCAL_PATH)/res
LOCAL_AAPT_FLAGS := --auto-add-overlay --extra-packages com.android.keyguard
# 编译成apk
include $(BUILD_PACKAGE)
Android4.4 SystemUI Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := $(call all-java-files-under, src) \
src/com/android/systemui/EventLogTags.logtags
LOCAL_JAVA_LIBRARIES := telephony-common
LOCAL_PACKAGE_NAME := SystemUI
LOCAL_CERTIFICATE := platform
LOCAL_PRIVILEGED_MODULE := true
LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
所以,系统调试锁屏,只需要单独编译SystemUI模块,然后替换SystemUI.apk即可。
如何替换系统锁屏,改为我们的锁屏应用?
解答:个人认为,如果能够修改SystemUI方法,那最好就是重载handleShow()方法,在这个方法中实现我们自己的锁屏界面。例如通过Activity跳转,别忘了释放PARTIAL_WEAK_LOCK。
重载这个方法,可以最大程度的不影响Android系统逻辑(ps:个人意见,大家有更好的办法可以指点我)。
位置:
framework/base/package/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
分析这个类,我们肯定是先从show方法开始入手。
show方法的源码如下:
public class StatusBarKeyguardViewManager {
public void show(Bundle options) {
// 设置keguard是否显示的标志
mShowing = true;
mStatusBarWindowManager.setKeyguardShowing(true);
// 重置view的状态,进行keyguard锁屏显示
reset();
}
}
源码如下:
public class StatusBarKeyguardViewManager {
public void reset() {
Log.e("TAG", "mShowing:" + mShowing + ", mOccluded:" + mOccluded);
if (mShowing) {
if (mOccluded) {
mPhoneStatusBar.hideKeyguard();
mBouncer.hide(false /* destroyView */);
} else {
// 判断是调用安全锁屏还是调用滑动锁屏
showBouncerOrKeyguard();
}
updateStates();
}
}
}
public class StatusBarKeyguardViewManager {
private void showBouncerOrKeyguard() {
if (mBouncer.needsFullscreenBouncer()) {
// The keyguard might be showing (already). So we need to hide it.
mPhoneStatusBar.hideKeyguard();
mBouncer.show(true);
} else {
mPhoneStatusBar.showKeyguard();
mBouncer.hide(false);
mBouncer.prepare();
}
}
}
接下来,就是view展示的过程了。其中,mBouncer是用来显示安全锁屏,例如图案、密码、PIN码等。有兴趣的同学可以继续跟踪一下mBouncer的展示或者mPhoneStatusBar的展示过程。