http://www.cnblogs.com/haiming/p/2989678.html html
前面对于MediaPlayer的系统研究,刚刚开始,因为其余缘由如今要先暂停一下。此次要看的模块是android 4.2 系统中的Keyguard模块。在接触以后才发现,android4.2的keyguard模块与以前相比,变化挺大的,最起码名字上变化挺大的。因为对于Android系统了解不是很深刻,并且知识和经验都比较弱,在文中确定有不恰当或者错误的地方,请各位路过的大神不吝指正。
在Android 4.2中,keyguard的模块存放的目录是在[/frameworks/base/policy/src/com/android/internal/policy/impl/keyguard/], 我也能够看到在同级目录下有个文件夹叫作keyguard_obsolete,这里的应该是在以前使用的版本,不过咱们今天的重点不是在它,而在keyguard目录。在Android系统中,说到解锁,最容易让人想起的是开机以后的滑动解锁画面,这个也是Android系统的默认的画面。咱们能够在Android手机的Settings应用中改变解锁方式,好比面部解锁,图形解锁,PIN码解锁,Password解锁等方式。在Android4.2中的解锁画面,又添加了新的内容,好比能够左右滑动,而且能够在解锁的画面上添加Calendar, Email,Clock,Message等不一样的widget。下面咱们就说在Android 4.2上的锁屏机制。java
先从解锁的画面提及。android
Keyguard的窗口从哪里来?web
Android系统中的画面及用户的交互,一般都是在Activity中进行的,不过这个Keyguard这块有点不一样。若是你在Keyguard模块的目录中进行搜索的话,是找不到Activity这样的关键字的。从这个搜索结果能够看出,Android系统中默认的keyguard最起码没有使用Activity。那么,keyguard的画面是如何呈现出来的呢?带着这个问题,开始Keyguard的探索之旅。第一次接触到Keyguard的时候,我不知道如何入手去看这块代码,我记得当时是当Power按下后,屏幕点亮时会显示出Keyguard。上次是从这个角度去了解Keyguard的,多少有点收获,也为再次看Keyguard模块打下点基础吧。之因此提及这个事情,是想告诫本身,求知不要心急,慢慢来不见得是坏事,时常回头看一眼,会有不同的收获。让子弹飞一会,不要着急。有了上次的些微了解以后,此次是从起源开始了解Keyguard是如何绘制出来的。咱们知道,Keyguard是系统启动以后就出现的(注:Android系统在标准状况下,刷机或格式化后的首次启动,是开始基本设置,即setupWizard),能够从这点入手。还有一点是,在Android系统中和Keyguard相关的类大可能是Keyguard开头。Android系统中的大管家SystemServer启动后,在一切准备稳当以后,会根据须要通知不一样的service.systemReady。Keyguard的启动就是从WindowManagerService的systemReady开始的,在WindowManagerService.systemReady()中会调用PhoneWindowManager的systemReady,由于PhoneWindowManager是WindowManagerPolicy的子类。在PhoneWindowManager中会判断KeyguarViewMediator是否已经初始化完成,其实在PhoneWindowManager的init的时候,这个对象就已经建立完毕。 这部分从SystemServer准备就绪,到WindowManagerService的systemReady,一直到PhoneWindowManager的systemReady都比较简单,这里就再也不列出代码。咱们知道在Keyguard的中已经存在了KeyguardViewManager了,为何还要KeyguardViewMediator呢? 咱们能够从KeyguardViewMediator的功能的角度看看这个缘由, 先看Android的设计者对这个类的描述:数据库
这是一个调解和keyguard相关请求的类。它包括了查询keyguard的状态,电源管理相关的事件,由于电源管理事件会影响keyguard的设置或重置,回调PhoneWindowManager通知它说keyguard正在显示,或者解锁成功等状态。注:keyguard画面是在屏幕关闭的时候显示的,因此当屏幕亮起来的时候,keyguard画面可以直接准备好了。好比,查询keyguard的例子:某个事件可以唤醒Keyguard吗?keyguard正在显示吗?某个事件会被锁屏的状态约束而不起做用吗?回调PhoneWindowManager的状况:锁屏正在显示。 致使锁屏状态变化的样例:屏幕关闭,重置锁屏,而且显示出来以便于下次屏幕亮起时可以直接显示。键盘打开,若是keyguard是不安全的,就隐藏它。从解锁画面发生的事件:用户成功解锁,隐藏解锁画面,再也不约束用户的输入事件。注:除了电源管理可以影响keyguard的状态外,其余的一些app或者service可能会经过方法setKeyguardEnable去关闭keyguard。好比接到电话时。这个类是在WindowManagerPolicy初始化的时候建立的,而且运行在WindowMangerPolicy所在的线程,keyguard的画面从这个线程中建立的当keyguardViewMediator构建时。可是Keyguard相关的api可能会被其余的线程调用,好比InputManagerService和windowManagerService。所以在keyguardViewMediator的方法是同步的,而且任何一个和Keyguard画面相关的事件都投掷到Handler中以确保在UI线程中处理。api
对KeyguardViewMediator有了大概的了解以后,咱们下面就直接从KeyguardViewMediator的onSystemReady开始分析Keyguard画面显示的过程,这个方法的代码以下:安全
1 /** 2 * Let us know that the system is ready after startup. 3 */ 4 public void onSystemReady() { 5 mSearchManager = (SearchManager) mContext.getSystemService(Context.SEARCH_SERVICE); 6 synchronized (this) { 7 if (DEBUG) Log.d(TAG, "onSystemReady"); 8 mSystemReady = true;//注册一个回调函数,用来监视系统中的用户切换变化,手机状态变化,sim卡状态变化等等 9 mUpdateMonitor.registerCallback(mUpdateCallback);10 11 // Suppress biometric unlock right after boot until things have settled if it is the12 // selected security method, otherwise unsuppress it. It must be unsuppressed if it is13 // not the selected security method for the following reason: if the user starts14 // without a screen lock selected, the biometric unlock would be suppressed the first15 // time they try to use it.16 //17 // Note that the biometric unlock will still not show if it is not the selected method.18 // Calling setAlternateUnlockEnabled(true) simply says don't suppress it if it is the19 // selected method.使用生物技术解锁,好比声纹解锁技术等。不常见20 if (mLockPatternUtils.usingBiometricWeak()21 && mLockPatternUtils.isBiometricWeakInstalled()) {22 if (DEBUG) Log.d(TAG, "suppressing biometric unlock during boot");23 mUpdateMonitor.setAlternateUnlockEnabled(false);24 } else {25 mUpdateMonitor.setAlternateUnlockEnabled(true);26 }27 28 doKeyguardLocked();//比较重要的一个方法29 }30 // Most services aren't available until the system reaches the ready state, so we31 // send it here when the device first boots.32 maybeSendUserPresentBroadcast();33 }
这个方法主要就是注册了一个监听系统中某些属性发生变化时的回调函数,再有就是开始锁屏。方法doKeyguardLocked的代码以下:app
1 private void doKeyguardLocked() { 2 doKeyguardLocked(null); 3 } 4 5 /** 6 * Enable the keyguard if the settings are appropriate. 7 */ 8 private void doKeyguardLocked(Bundle options) { 9 // 若是有其余的应用关闭了keyguard的话,不用显示10 if (!mExternallyEnabled) {11 if (DEBUG) Log.d(TAG, "doKeyguard: not showing because externally disabled");12 13 // note: we *should* set mNeedToReshowWhenReenabled=true here, but that makes14 // for an occasional ugly flicker in this situation:15 // 1) receive a call with the screen on (no keyguard) or make a call16 // 2) screen times out17 // 3) user hits key to turn screen back on18 // instead, we reenable the keyguard when we know the screen is off and the call19 // ends (see the broadcast receiver below)20 // TODO: clean this up when we have better support at the window manager level21 // for apps that wish to be on top of the keyguard22 return;23 }24 25 // 若是keyguard正在显示的话,就不用再向下执行了。26 if (mKeyguardViewManager.isShowing()) {27 if (DEBUG) Log.d(TAG, "doKeyguard: not showing because it is already showing");28 return;29 }30 31 // if the setup wizard hasn't run yet, don't show32 final boolean requireSim = !SystemProperties.getBoolean("keyguard.no_require_sim",33 false);34 final boolean provisioned = mUpdateMonitor.isDeviceProvisioned();35 final IccCardConstants.State state = mUpdateMonitor.getSimState();36 final boolean lockedOrMissing = state.isPinLocked()37 || ((state == IccCardConstants.State.ABSENT38 || state == IccCardConstants.State.PERM_DISABLED)39 && requireSim);40 41 if (!lockedOrMissing && !provisioned) {42 if (DEBUG) Log.d(TAG, "doKeyguard: not showing because device isn't provisioned"43 + " and the sim is not locked or missing");44 return;45 }46 47 if (mUserManager.getUsers(true).size() < 248 && mLockPatternUtils.isLockScreenDisabled() && !lockedOrMissing) {49 if (DEBUG) Log.d(TAG, "doKeyguard: not showing because lockscreen is off");50 return;51 }52 53 if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");54 showLocked(options);//开始显示keyguard画面,注意这里的参数是null55 }
经过这个来决定keyguard是否应该显示,在正常状况下(非来电,非多用户,非首次启动等等),决定显示keyguard画面,而后把这个发送消息到mHandler,不过都是在WindowManagerPolicy的线程中,使用的是同一个线程进行的loop。在显示画面的过程当中,要一直保持设备处于亮着状态。处理SHOW消息是由方法handleShow完成的,代码以下:ide
1 private void handleShow(Bundle options) { 2 synchronized (KeyguardViewMediator.this) { 3 if (DEBUG) Log.d(TAG, "handleShow"); 4 if (!mSystemReady) return; 5 //调用KeyguardviewManager进行画面的显示 6 mKeyguardViewManager.show(options); 7 mShowing = true; 8 mKeyguardDonePending = false; 9 updateActivityLockScreenState();//和ActivityManagerService交互10 adjustStatusBarLocked();//调整StatusBar中显示的一些内容,disable一些在statusBar中进行的操做11 userActivity(); //通知PowerManagerService有用户事件发生,及时更新屏幕超时时间为10s12 try {13 ActivityManagerNative.getDefault().closeSystemDialogs("lock");14 } catch (RemoteException e) {15 }16 17 // 播放解锁的完成的声音。18 playSounds(true);19 //在发送SHOW消息的时候,申请过这个wakelock,在这里释放20 mShowKeyguardWakeLock.release();21 }22 }
在这个方法中主要就是调用KeyguardViewManager,让其去显示咱们所须要的UI.在完成现实以后,处理一些必要事情,好比更新KeyguardViewMediator的一些属性,和ActivityManagerService,StatusBarService,PowerManagerService等的交互和播放解锁声。看来画面的显示还在下一步,咱们接着往下看:函数
[KeyguardViewManager.java]
1 public synchronized void show(Bundle options) {//此时参数为null 2 if (DEBUG) Log.d(TAG, "show(); mKeyguardView==" + mKeyguardView); 3 //在手机上返回值应该是false 4 boolean enableScreenRotation = shouldEnableScreenRotation(); 5 //这里就是建立Keyguard UI的地方。 6 maybeCreateKeyguardLocked(enableScreenRotation, false, options); 7 maybeEnableScreenRotation(enableScreenRotation); 8 9 // Disable common aspects of the system/status/navigation bars that are not appropriate or10 // useful on any keyguard screen but can be re-shown by dialogs or SHOW_WHEN_LOCKED11 // activities. Other disabled bits are handled by the KeyguardViewMediator talking12 // directly to the status bar service.13 final int visFlags = View.STATUS_BAR_DISABLE_HOME;14 if (DEBUG) Log.v(TAG, "show:setSystemUiVisibility(" + Integer.toHexString(visFlags)+")");15 mKeyguardHost.setSystemUiVisibility(visFlags);16 17 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);18 mKeyguardHost.setVisibility(View.VISIBLE);19 mKeyguardView.show();20 mKeyguardView.requestFocus();21 }
这个方法的主要功能就是建立一个可以承载KeyguardView的地方,而后把这个View给显示出来。咱们先看看建立可以承载KeyguardView的方法maybeCreateKeyguardLocked,代码以下:
1 private void maybeCreateKeyguardLocked(boolean enableScreenRotation, boolean force, 2 Bundle options) { 3 final boolean isActivity = (mContext instanceof Activity); // 这里的mContext是来自SystemServer的Context,所以返回值应该是false 4 5 if (mKeyguardHost != null) { 6 mKeyguardHost.saveHierarchyState(mStateContainer); 7 } 8 9 if (mKeyguardHost == null) {//这里分析的是开机以后的Keyguard的第一次显示,mKeyguardHost应该是null。所以会进入这个判断分支10 if (DEBUG) Log.d(TAG, "keyguard host is null, creating it...");11 12 mKeyguardHost = new ViewManagerHost(mContext);13 //建立承载Keyguard的窗口的属性14 int flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN15 | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR16 | WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN17 | WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;18 19 if (!mNeedsInput) {20 flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;21 }22 if (ActivityManager.isHighEndGfx()) {23 flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;24 }25 26 final int stretch = ViewGroup.LayoutParams.MATCH_PARENT;27 final int type = isActivity ? WindowManager.LayoutParams.TYPE_APPLICATION28 : WindowManager.LayoutParams.TYPE_KEYGUARD;//Type应该是WindowManager.LayoutParams.TYPE_KEYGUARD29 WindowManager.LayoutParams lp = new WindowManager.LayoutParams(30 stretch, stretch, type, flags, PixelFormat.TRANSLUCENT);31 lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;32 lp.windowAnimations = com.android.internal.R.style.Animation_LockScreen;33 lp.screenOrientation = enableScreenRotation ?34 ActivityInfo.SCREEN_ORIENTATION_USER : ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;35 36 if (ActivityManager.isHighEndGfx()) {37 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;38 lp.privateFlags |=39 WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_HARDWARE_ACCELERATED;40 }41 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SET_NEEDS_MENU_KEY;42 if (isActivity) {43 lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;44 }45 lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY;46 lp.setTitle(isActivity ? "KeyguardMock" : "Keyguard");47 mWindowLayoutParams = lp;48 mViewManager.addView(mKeyguardHost, lp);//注意这里,若是阅读过Activity启动相关的代码的话,这个地方会很熟悉49 }50 51 if (force || mKeyguardView == null) {52 inflateKeyguardView(options);53 mKeyguardView.requestFocus();54 }55 updateUserActivityTimeoutInWindowLayoutParams();56 mViewManager.updateViewLayout(mKeyguardHost, mWindowLayoutParams);57 58 mKeyguardHost.restoreHierarchyState(mStateContainer);59 }
在这个方法中,首先判断目前使用的解锁方式是以应用的方式提供的仍是系统默认的,若是是以应用程序方式提供的解锁,那么它的Context应该是是一个Activity。咱们这里讨论的是系统自带的解锁方式,因此isActivity的值应该是false的。这是系统启动以后的首次开始解锁,因此KeyguardViewManager中的属性除了在构造函数中初始化过的,其余均应为null或未初始化。接下来由于mKeyguardHost尚未建立,因此,接下来就是建立ViewManagerHost的实例,注意,这里是的mKeyguardHost是ViewManagerHost的实例,它是KeyguardViewManager的一个内部类,继承自FrameLayout类。而后定义Keyguard窗口对应的一些特定的属性,这些属性中尤为注意type类型是WindowManager.LayoutParams.TYPE_KEYGUARD,这是一个系统级别的窗口,关于这个属性的使用,在Activity启动过程当中图像的描绘时会仔细讨论的。把所须要的参数都定义完成以后,就开始把ViewManagerHost最终经过WindowManagerImpl添加到窗口中。添加过程不是本次的重点,这里忽略。后面的代码不是很复杂,很容易明白,就再也不一一介绍。到这里咱们的目的已经达到了,回头想一下,咱们是为了寻找Keyguard的画面是在没有Activity的状况下如何呈现出来的,这里已经给出了答案,把ViewManagerHost添加到窗口中,这里的ViewManagerHost是继承自FrameLayout的,至关于一个view的容器,只要把咱们所须要的View布局到这个容器中,系统会帮助咱们完成绘制的过程。关于View的绘制过程,网上有不少文章分析Activity的启动过程,或者分析WindowManageService的时候都会说到这个问题,这些内容不是本次的重点,这里略过。
KeyguardView是如何布局到窗口?
咱们已经拥有了View的容器了,接下来咱们能够分析Keyguard的是如何把不一样的解锁方式的画面放进这个容器的,也就是咱们要开始分析Android系统各类解锁画面是如何布局到咱们获得的容器中的。下面咱们接着上面的addView以后分析。咱们一直分析到这儿,在上面代码中51line,mKeyguardView尚未见到,在这里判断条件是成立的,接着执行inflateKeyguardView。从这个方法的名字能够看出,这就是把view布局到窗口的函数,而他的参数是咱们以前已经说明的为null。让咱们一块儿看看是如何布局keyguardview的,其代码以下:
1 private void inflateKeyguardView(Bundle options) { 2 View v = mKeyguardHost.findViewById(R.id.keyguard_host_view); 3 if (v != null) {//若是已经把keyguardhost布局到ViewManagerHostview中的话,这里再把它清除掉 4 mKeyguardHost.removeView(v); 5 } 6 // TODO: Remove once b/7094175 is fixed 7 if (false) Slog.d(TAG, "inflateKeyguardView: b/7094175 mContext.config=" 8 + mContext.getResources().getConfiguration()); 9 final LayoutInflater inflater = LayoutInflater.from(mContext);10 View view = inflater.inflate(R.layout.keyguard_host_view, mKeyguardHost, true);//把KeyguardHostView布局到ViewManagerHost中,当把View布局完成后,会调用子view的onfinishInflate方法11 mKeyguardView = (KeyguardHostView) view.findViewById(R.id.keyguard_host_view);12 mKeyguardView.setLockPatternUtils(mLockPatternUtils);13 mKeyguardView.setViewMediatorCallback(mViewMediatorCallback);14 mKeyguardView.initializeSwitchingUserState(options != null &&15 options.getBoolean(IS_SWITCHING_USER));16 17 // HACK18 // The keyguard view will have set up window flags in onFinishInflate before we set19 // the view mediator callback. Make sure it knows the correct IME state.20 if (mViewMediatorCallback != null) {//若是这个判断条件成立,仅仅是找到某个View而已,尚未开始show21 KeyguardPasswordView kpv = (KeyguardPasswordView) mKeyguardView.findViewById(22 R.id.keyguard_password_view);23 24 if (kpv != null) {25 mViewMediatorCallback.setNeedsInput(kpv.needsInput());26 }27 }28 29 if (options != null) {30 int widgetToShow = options.getInt(LockPatternUtils.KEYGUARD_SHOW_APPWIDGET,31 AppWidgetManager.INVALID_APPWIDGET_ID);32 if (widgetToShow != AppWidgetManager.INVALID_APPWIDGET_ID) {33 mKeyguardView.goToWidget(widgetToShow);34 }35 }36 }
首先判断在ViewManagerHost是否是已经存在KeyguardHostView了,若是已经存在了,先把它移除了,而后从新把它布局到ViewManagerHost中。在布局完成后调用KeyguardHostView的onFinishInflate方法。
接着为KeyguardHostView设置回调函数和属性。既然要把KeyguardHostView布局到咱们获得的容器中,咱们就看看KeyguardHostView的结构式怎样的。keyguardHostView对应的布局文件在
[/frameworks/base/core/res/res/layout-port/keyguard_host_view.xml],这个布局文件对应的ViewTree以下:
这个就是咱们Android系统中KeyguardHostView的view树。咱们知道在Android4.2的keyguard系统中,在锁屏界面是能够左右滑动的。这种滑动是经过ViewFlipper实现的,这里的keyguard_widget_remove_drop_target是用来添加widget的容器,对应的画面是带有加号,可以添加widget的画面。 而在keyguard_widget_paper中能够盛放各类widget,好比时钟,邮件等。而咱们常用的解锁方式是放在keyguardSecurityContainer中的。到这里,咱们貌似获得仍是一个View容器。只不过是把KeyguardHostView这个容器布局到ViewManagerHost这个容器中,到如今仍是没有见到咱们的解锁方式是如何呈现的。先别急,接下来咱们就分析,各类view是如何取得的。以前咱们是为了寻找keyguard的窗口是如何得到的,而后咱们获得了这个窗口,而且还获得了一个view容器,而后在这个容器中又布局了一个容器KeyguardHostView,如今咱们一步一步的return到前面的调用的函数,一直回到keyguardViewManager.show()方法,这个方法在取得可以盛放view的容器KeyguardHostView后开始调用KeyguardHostView的show函数,及mKeyguardView.show(),代码以下:
1 public void show() {//为了缩减文章的长度,把代码中一些log,注释和一些没必要要的代码给删除了,并不影响对keyguard的理解 2 showPrimarySecurityScreen(false); 3 } 4 void showPrimarySecurityScreen(boolean turningOff) { 5 SecurityMode securityMode = mSecurityModel.getSecurityMode();//获取当前的锁屏的方式,经过查找数据库,等会重点分析此函数 6 showSecurityScreen(securityMode); 7 } 8 9 private void showSecurityScreen(SecurityMode securityMode) {10 if (securityMode == mCurrentSecuritySelection) return;11 //注意,这里的变量的类型,是KeyguardSecrityView,根据securityMode去获取相应的View12 KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);13 KeyguardSecurityView newView = getSecurityView(securityMode);14 15 // Enter full screen mode if we're in SIM or Account screen16 boolean fullScreenEnabled = getResources().getBoolean(17 com.android.internal.R.bool.kg_sim_puk_account_full_screen);18 boolean isSimOrAccount = securityMode == SecurityMode.SimPin19 || securityMode == SecurityMode.SimPuk20 || securityMode == SecurityMode.Account;21 mAppWidgetContainer.setVisibility(22 isSimOrAccount && fullScreenEnabled ? View.GONE : View.VISIBLE);23 24 if (mSlidingChallengeLayout != null) {//mSlidingChallengeLayout对应上面ViewTree中的SlidingChallengeLayout25 mSlidingChallengeLayout.setChallengeInteractive(!fullScreenEnabled);26 }27 28 if (oldView != null) {29 oldView.onPause();//模拟生命周期的方式来管理各个解锁画面30 oldView.setKeyguardCallback(mNullCallback); // ignore requests from old view31 }32 newView.onResume(KeyguardSecurityView.VIEW_REVEALED);33 newView.setKeyguardCallback(mCallback);34 35 final boolean needsInput = newView.needsInput();36 if (mViewMediatorCallback != null) {37 mViewMediatorCallback.setNeedsInput(needsInput);38 }39 40 // Find and show this child.41 final int childCount = mSecurityViewContainer.getChildCount();42 //切换时的动画,新的view进入的动画方式,前一个view退出的动画方式43 mSecurityViewContainer.setInAnimation(44 AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_in));45 mSecurityViewContainer.setOutAnimation(46 AnimationUtils.loadAnimation(mContext, R.anim.keyguard_security_fade_out));47 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);48 for (int i = 0; i < childCount; i++) {49 if (mSecurityViewContainer.getChildAt(i).getId() == securityViewIdForMode) {50 mSecurityViewContainer.setDisplayedChild(i);//这个方法会把选择的view设置上,而且会调用requestLayout把画面更新出来51 break;52 }53 }54 55 if (securityMode == SecurityMode.None) {56 // Discard current runnable if we're switching back to the selector view57 setOnDismissAction(null);58 }59 if (securityMode == SecurityMode.Account && !mLockPatternUtils.isPermanentlyLocked()) {60 // we're showing account as a backup, provide a way to get back to primary61 setBackButtonEnabled(true);62 }63 mCurrentSecuritySelection = securityMode;64 }
在选择画面以前,要先肯定咱们锁屏的方式,即SecurityMode,这个mode就是对应各类各样的解锁方式。若是不作修改的话,设置以后,不管是手机是否重启,都会保持这个模式不变,除非本身手动在Settings应用中的锁屏方式中作出修改。所以,咱们能够肯定锁屏的方式必定是以某种方式存储到磁盘上了,经过SecurityModel.getSecurityMode()能够读取以前存储的锁屏模式,其代码以下:
1 [SecurityMode.java] 2 SecurityMode getSecurityMode() { 3 KeyguardUpdateMonitor updateMonitor = KeyguardUpdateMonitor.getInstance(mContext); 4 final IccCardConstants.State simState = updateMonitor.getSimState();//获取SIM卡的状态,查看SIM是否被锁住 5 SecurityMode mode = SecurityMode.None;//这里的每个SecurityMode都对应一个view, 6 if (simState == IccCardConstants.State.PIN_REQUIRED) { 7 mode = SecurityMode.SimPin; 8 } else if (simState == IccCardConstants.State.PUK_REQUIRED 9 && mLockPatternUtils.isPukUnlockScreenEnable()) {10 mode = SecurityMode.SimPuk;11 } else {//经过LockPatternUtils和LockSettingsService通讯,打开locksettings.db数据库,读取保存的解锁方式12 final int security = mLockPatternUtils.getKeyguardStoredPasswordQuality();13 switch (security) {14 case DevicePolicyManager.PASSWORD_QUALITY_NUMERIC:15 mode = mLockPatternUtils.isLockPasswordEnabled() ?16 SecurityMode.PIN : SecurityMode.None;17 break;18 case DevicePolicyManager.PASSWORD_QUALITY_ALPHABETIC:19 case DevicePolicyManager.PASSWORD_QUALITY_ALPHANUMERIC:20 case DevicePolicyManager.PASSWORD_QUALITY_COMPLEX:21 mode = mLockPatternUtils.isLockPasswordEnabled() ?22 SecurityMode.Password : SecurityMode.None;23 break;24 25 case DevicePolicyManager.PASSWORD_QUALITY_SOMETHING:26 case DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED:27 if (mLockPatternUtils.isLockPatternEnabled()) {28 mode = mLockPatternUtils.isPermanentlyLocked() ?29 SecurityMode.Account : SecurityMode.Pattern;30 }31 break;32 33 default:34 throw new IllegalStateException("Unknown unlock mode:" + mode);35 }36 }37 return mode;38 }39 [KeyguardHostView.java]40 private int getSecurityViewIdForMode(SecurityMode securityMode) {41 switch (securityMode) {42 case None: return R.id.keyguard_selector_view;//这个对应的是默认的解锁方式,即滑动的方式解锁43 case Pattern: return R.id.keyguard_pattern_view;44 case PIN: return R.id.keyguard_pin_view;45 case Password: return R.id.keyguard_password_view;46 case Biometric: return R.id.keyguard_face_unlock_view;47 case Account: return R.id.keyguard_account_view;48 case SimPin: return R.id.keyguard_sim_pin_view;49 case SimPuk: return R.id.keyguard_sim_puk_view;50 }51 return 0;52 }
在经过LockPatternUtils去查找数据库的时候,须要和LockSettingsService通讯,这里就体现了锁屏的一个重要的保护措施,对调用这个方法的UserId进行严格的检查。经过这种方式得到解锁方式,而后找到这个解锁方式对应的画面,即KeyguardSecurityView。在getSecurityViewForMode中仅仅是获取到这些view的id,在getSecurityMode方法中还要把这些view布局到KeyguardSecurityViewContainer中,实现的代码以下:
1 private KeyguardSecurityView getSecurityView(SecurityMode securityMode) { 2 final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);//根据SecurityMode获取对应的view的Id 3 KeyguardSecurityView view = null; 4 final int children = mSecurityViewContainer.getChildCount();//在开始的时候,mSecurityViewContainer中应该是没有任何view,这里的初始值应该为0 5 for (int child = 0; child < children; child++) { 6 if (mSecurityViewContainer.getChildAt(child).getId() == securityViewIdForMode) { 7 view = ((KeyguardSecurityView)mSecurityViewContainer.getChildAt(child)); 8 break; 9 }10 }11 int layoutId = getLayoutIdFor(securityMode);//根据SecurityMode获取对应的布局文件12 if (view == null && layoutId != 0) {13 final LayoutInflater inflater = LayoutInflater.from(mContext);14 if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);15 View v = inflater.inflate(layoutId, mSecurityViewContainer, false);//把布局文件布局到KeyguardSecurityViewContainer中16 mSecurityViewContainer.addView(v);//把找到的view添加到KeyguardSecurityViewContainer中17 updateSecurityView(v);18 view = (KeyguardSecurityView)v;19 }20 21 if (view instanceof KeyguardSelectorView) {22 KeyguardSelectorView selectorView = (KeyguardSelectorView) view;23 View carrierText = selectorView.findViewById(R.id.keyguard_selector_fade_container);24 selectorView.setCarrierArea(carrierText);25 }26 27 return view;//注意,返回值的类型是keyguardSecurityView28
在继续分析view是如何显示的以前,咱们还要再补充一点,关于各类keyguard的画面和KeyguardSecurityView之间的关系。以下几种典型的解锁方式,及其结构关系图:
上面这幅图仅仅是Keyguard解锁方式和View相关的类图。不过图中的类KeyguardSelector这个类从名字上不容易和咱们一直的解锁方式的画面对应起来,其实这类中包含的是GlowPadView,就是在Android4.2开机以后默认的以滑动方式解锁。不过呢,这样咱们从这幅图中仍是能够看到,解锁方式一共有五大类,分别是KeyguardFaceLockedView,KeyguardAccountView,KeyguardPatternView,KeyguardAbsInputView,GlowPadView。其中全部的须要输入数字或者字符的解锁方式都归为KeyguardAbsInputView。从这幅图中,咱们还能够发现特色就是,这些解锁 画面有两个共同的基类,一个是KeyguardSecurityView,一个是LinearLayout。这两个基类中,keyguardSecurityView是为了让keyguard系统便于管理这些画面而存在;LinearLayout是为了让这些View可以正常地使用View系统描绘而存在的。因此只要KeyguardViewManager持有KeyguardSecurityView这个基类,就能够指向其任一子类的实例,即不一样的解锁画面。这是面向对象的多态性。至于要使用的那个解锁方式,是经过LockPatternUtils和LockSettingsService通讯,读取数据库来得到的。
如何解锁?
能够分为密码类型的解锁方式,图案类型的解锁方式。这部份内容,经过代码很容易能看明白,就再也不叙述