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通訊,讀取數據庫來得到的。
如何解鎖?
能夠分爲密碼類型的解鎖方式,圖案類型的解鎖方式。這部份內容,經過代碼很容易能看明白,就再也不敘述