目录
目录 锁屏时序图 开机启动到PhoneWindowManager的systemReady方法 锁屏加载流程 PhoneWindowManager KeyguardServiceDelegate KeyguardServiceWrapper KeyguardService KeyguardViewMediator onSystemReady onKeyguardLocked showLocked handleShow 重点一 重点二 StatusBarKeyguardViewManager show reset showBouncerOrKeyguard 锁屏时序图 研究了将近两天的Android5.1 Keyguard锁屏机制,不得不说,各种饶。这里先把锁屏流程时序图贡献给大家: 使用的是线编辑工具ProcessOn,用来编辑时序图效果看起来不是太好。不过没有太大关系,这个时序图只是为了方便我们能清晰的对锁屏流程有个大致的了解,接下来,我会详细的分析每个类的具体流程。声明:本文基于AndroidLollipop 5.1.1_r6版本进行的源码分析。
开机启动到PhoneWindowManager的systemReady方法
准备先从开机启动到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,我们也就是按照时序图,根据时序图上的每个类,进行相关源码分析(重点难点的源码我会中文注释)。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(); // ... 省略不相关源码 } } KeyguardServiceDelegate 路径: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; } } } KeyguardServiceWrapper 路径: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() {
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(); } }public void onServiceDisconnected(ComponentName name) { if (DEBUG) Log.v(TAG, "*** Keyguard disconnected (boo!)"); mKeyguardService = null; } }; } 接下来,就需要去看一下KeyguardService类的onSystemReady方法了。
KeyguardService
路径:/frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java
源码:
public class KeyguardService extends Service {
private KeyguardViewMediator mKeyguardViewMediator;// Binder interface
public void onSystemReady() { // 检查调用进程是否具体SYSTEM权限 checkPermission(); // 真正的锁屏入口 mKeyguardViewMediator.onSystemReady(); } } Wow,终于到了我们的主角KeyguardViewMediator登场了。KeyguardViewMediator
路径:frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
虽然KeyguardViewMediator是锁屏的入口,但是从这里到锁屏的真正展现还有很长一段路。接下来,为了方便,我们都是基于当前类的函数进行分析。
onSystemReady
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); } } } onKeyguardLocked() 其中,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。showLocked()
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(); } } } handleShow() 既然是Handler Message机制,那我们就要去看一下mHandler类实例是如何处理SHOW类型的消息了。mHandle处理SHOW类型消息的方法如下:public class KeyguardViewMediator extends SystemUI {
private Handler mHandler = new Handler(Looper.myLooper(), null /*callback*/, true /*async*/) { 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 := KeyguardLOCAL_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.mkLOCAL_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:个人意见,大家有更好的办法可以指点我)。
StatusBarKeyguardViewManager
位置:framework/base/package/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
分析这个类,我们肯定是先从show方法开始入手。
show()
show方法的源码如下:public class StatusBarKeyguardViewManager {
public void show(Bundle options) { // 设置keguard是否显示的标志 mShowing = true; mStatusBarWindowManager.setKeyguardShowing(true); // 重置view的状态,进行keyguard锁屏显示 reset(); } } 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(); } } } showBouncerOrKeyguard() 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的展示过程。设置开机默认无keyguard
阅读数 58设置开机默认无keyguard文章目录设置开机默认无keyguard1.Description2.Analysis3.solution4.summary1.Description默认情况下keygua...
博文