Android P keyguard 初始化,Pattern解鎖等介紹

Android P keyguard 初始化,Pattern解鎖等介紹

學習就需要帶着問題去看書看源碼,針對keyguard 有如下幾個問題:

問題1:keyguard是什麼?

問題2:keyguard的初始化流程

問題3:Pattern驗證方式的解鎖流程?

本文使用到的調試方法,請點擊查看 -> Android Framework 常用的調試方式

keyguard是什麼

KeyGuard源代碼目錄結構

​ 在老早的版本中keyguard是一個獨立的APK,後來keyguard與systemui合併,keyguard是SystemUI的一個庫。再後來到現在,在Android P 中,keyguard的目錄結構與SystemUI同級。

代碼文件的目錄結構如下:

:~/code/code/android-9.0.0_r3/frameworks/base/packages/SystemUI$ tree -L 1
.
├── AndroidManifest.xml
├── Android.mk
├── docs
├── lint.xml
├── MODULE_LICENSE_APACHE2
├── NOTICE
├── OWNERS
├── plugin
├── proguard.flags
├── README.md
├── res             ->systemui 的資源文件
├── res-keyguard    ->keyguard 的資源文件
├── scripts
├── shared
├── src
└── tests

~/code/code/android-9.0.0_r3/frameworks/base/packages/SystemUI$ tree -L 1 src/com/android/
src/com/android/
├── keyguard
└── systemui

其Android.mk 如下:

frameworks/base/packages/SystemUI/Android.mk
32 LOCAL_SRC_FILES := \
33     $(call all-java-files-under, src) \
34     $(call all-Iaidl-files-under, src) \
35     $(call all-Iaidl-files-under, $(RELATIVE_FINGERPRINT_PATH))
...
68 LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res-keyguard $(LOCAL_PATH)/res

此處在 LOCAL_SRC_FILES 就直接包含了systemui與keyguard的代碼文件
LOCAL_RESOURCE_DIR 中也添加了systemui的資源文件,與keyguard的資源文件。

KeyGuard Layout佈局

簡要的說keyguard主要是爲實現如下兩個截圖而做的一系列功能。

在這裏插入圖片描述

在這裏插入圖片描述

​ 這兩個截屏,其中第一個截圖爲:notification_panel–> R.layout.status_bar_expanded

第二個截圖爲:keyguardBouncer–>R.layout.keyguard_bouncer

​ 這兩個界面其都是掛在mStatusBarWindow 這個頂級view裏面的。他們會根據實際的情況進行隱藏、縮放、透明度的調整等。

notification_panel 的佈局如何加到 mStatusBarWindow 這個頂級view裏面的。請看如下:

frameworks/base/packages/SystemUI/res/layout/super_status_bar.xml
...
64     <include layout="@layout/status_bar_expanded"
65         android:layout_width="match_parent"
66         android:layout_height="match_parent"
67         android:visibility="invisible" />
...
<!-- 默認 status_bar_expanded 在super_status_bar 佈局裏面本include的,且默認不顯示。 -->

比如上面第一個截圖中的正在充電的那個字符串(charged)由如下顯示:
/packages/SystemUI/res/layout/status_bar_expanded.xml
81     <include
82         layout="@layout/keyguard_bottom_area"
83         android:visibility="gone" />
//  添加keyguard頁面的bottom佈局,keyguard_bottom_area

/packages/SystemUI/res/layout/keyguard_bottom_area.xml
47         <com.android.systemui.statusbar.phone.KeyguardIndicationTextView
48             android:id="@+id/keyguard_indication_text"
49             android:layout_width="match_parent"
50             android:layout_height="wrap_content"
51             android:gravity="center_horizontal"
52             android:textStyle="italic"
53             android:textColor="?attr/wallpaperTextColorSecondary"
54             android:textSize="16sp"
55             android:textAppearance="?android:attr/textAppearanceSmall"
56             android:accessibilityLiveRegion="polite" />
// KeyguardIndicationTextView 

/SystemUI/src/com/android/systemui/statusbar/KeyguardIndicationController.java
382      private String computePowerIndication() {
383          if (mPowerCharged) {
384              return mContext.getResources().getString(R.string.keyguard_charged);
385          }

/SystemUI/res-keyguard/values/strings.xml
<string name="keyguard_charged">Charged</string>
函數 computePowerIndication 能夠獲取到充電狀態的信息,然後顯示在 keyguard_bottom_area 位置上。

​ 而keyguardBouncer卻並沒有被默認添加到頂級status_bar 裏面。而keyguardBouncer是如何加到status bar裏面去的呢,請看如下:

SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
625      @Override
626      public void start() {
...
777          startKeyguard();   // 在statusbar啓動的時候,就會去啓動keyguard相關流程。
778  
779          KeyguardUpdateMonitor.getInstance(mContext).registerCallback(mUpdateCallback);
780          putComponent(DozeHost.class, mDozeServiceHost);
....


1310      protected void startKeyguard() {
1311          Trace.beginSection("StatusBar#startKeyguard");
1312          KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
...
1317                  getBouncerContainer(), mNotificationPanel, mFingerprintUnlockController);    //  此處 keyguardViewMediator.registerStatusBar 。而 getBouncerContainer 其實就是 mStatusBarWindow
...
1320          mFingerprintUnlockController.setStatusBarKeyguardViewManager(mStatusBarKeyguardViewManager);
1326          Trace.endSection();
1327      }
.....
1337      protected ViewGroup getBouncerContainer() {
1338          return mStatusBarWindow;
1339      }
....

SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
141      public void registerStatusBar(StatusBar statusBar,
142              ViewGroup container,
143              NotificationPanelView notificationPanelView,
144              FingerprintUnlockController fingerprintUnlockController,
145              DismissCallbackRegistry dismissCallbackRegistry) {
146          mStatusBar = statusBar;
147          mContainer = container;
148          mFingerprintUnlockController = fingerprintUnlockController;
149          mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext,
150                  mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry,
151                  mExpansionCallback);    //此處創建 keyguardBouncer 。
152          mContainer.addOnLayoutChangeListener(this::onContainerLayout);
153          mNotificationPanelView = notificationPanelView;
154          notificationPanelView.setExpansionListener(this::onPanelExpansionChanged);
155      }

SystemUI/src/com/android/systemui/SystemUIFactory.java
99      public KeyguardBouncer createKeyguardBouncer(Context context, ViewMediatorCallback callback,
100              LockPatternUtils lockPatternUtils,  ViewGroup container,
101              DismissCallbackRegistry dismissCallbackRegistry,
102              KeyguardBouncer.BouncerExpansionCallback expansionCallback) {
103          return new KeyguardBouncer(context, callback, lockPatternUtils, container,
104                  dismissCallbackRegistry, FalsingManager.getInstance(context), expansionCallback);
105      }

​ 所以KeyGuard 從佈局上來說是屬於statusbar的一部分。當然爲了實現這個keyguard,還有很多其他的代碼,比如後面會提到的keyguard的初始化流程會涉及到一大波其他內容。

keyguard的初始化流程

起點是在 SystemUI/src/com/android/systemui/SystemUIService.java

onCreate() -> ((SystemUIApplication) getApplication()).startServicesIfNeeded();

/packages/SystemUI/src/com/android/systemui/SystemUIApplication.java
127      public void startServicesIfNeeded() {
128          String[] names = getResources().getStringArray(R.array.config_systemUIServiceComponents);
129          startServicesIfNeeded(names);
130      }

//此處讀取 SystemUI Services 的列表,然後依次啓動。其列表如下。
/packages/SystemUI/res/values/config.xml
330     <!-- SystemUI Services: The classes of the stuff to start. -->
331     <string-array name="config_systemUIServiceComponents" translatable="false">
332         <item>com.android.systemui.Dependency</item>
333         <item>com.android.systemui.util.NotificationChannels</item>
334         <item>com.android.systemui.statusbar.CommandQueue$CommandQueueStart</item>
335         <item>com.android.systemui.keyguard.KeyguardViewMediator</item>
336         <item>com.android.systemui.recents.Recents</item>
337         <item>com.android.systemui.volume.VolumeUI</item>
338         <item>com.android.systemui.stackdivider.Divider</item>
339         <item>com.android.systemui.SystemBars</item>
340         <item>com.android.systemui.usb.StorageNotification</item>
341         <item>com.android.systemui.power.PowerUI</item>
342         <item>com.android.systemui.media.RingtonePlayer</item>
343         <item>com.android.systemui.keyboard.KeyboardUI</item>
344         <item>com.android.systemui.pip.PipUI</item>
345         <item>com.android.systemui.shortcut.ShortcutKeyDispatcher</item>
346         <item>@string/config_systemUIVendorServiceComponent</item>
347         <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
348         <item>com.android.systemui.LatencyTester</item>
349         <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
350         <item>com.android.systemui.ScreenDecorations</item>
351         <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
352         <item>com.android.systemui.SliceBroadcastRelayHandler</item>
353     </string-array>
從這個列表中,可以看出 keyguard.KeyguardViewMediator 是keyguard 的 SystemUI服務入口。 

其他的SystemUI Service 並不是我們的關注對象,着重com.android.systemui.keyguard.KeyguardViewMediator 啓動流程

/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
768      public void start() {
769          synchronized (this) {
770              setupLocked();
771          }
772          putComponent(KeyguardViewMediator.class, this);
773      }
...
688      private void setupLocked() {
689          mPM = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
690          mTrustManager = (TrustManager) mContext.getSystemService(Context.TRUST_SERVICE);
691  
692          mShowKeyguardWakeLock = mPM.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "show keyguard");
693          mShowKeyguardWakeLock.setReferenceCounted(false);
...
699          final IntentFilter delayedActionFilter = new IntentFilter();
700          delayedActionFilter.addAction(DELAYED_KEYGUARD_ACTION);
701          delayedActionFilter.addAction(DELAYED_LOCK_PROFILE_ACTION);
702          mContext.registerReceiver(mDelayedLockBroadcastReceiver, delayedActionFilter,
703                  SYSTEMUI_PERMISSION, null /* scheduler */);
...
705          mKeyguardDisplayManager = new KeyguardDisplayManager(mContext, mViewMediatorCallback);
706  
707          mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
708  
709          mUpdateMonitor = KeyguardUpdateMonitor.getInstance(mContext);
710  
711          mLockPatternUtils = new LockPatternUtils(mContext);
727          mStatusBarKeyguardViewManager =
728                  SystemUIFactory.getInstance().createStatusBarKeyguardViewManager(mContext,
729                          mViewMediatorCallback, mLockPatternUtils);
730          final ContentResolver cr = mContext.getContentResolver();
731  
732          mDeviceInteractive = mPM.isInteractive();
...
778      public void onSystemReady() {
779          mHandler.obtainMessage(SYSTEM_READY).sendToTarget();
780      }

從這個 setupLocked() 方法中,看得出來,獲取了需要的系統服務的實例,註冊了廣播,註冊了監聽器。其實就是一個所需系統的參數初始化,該文件的功能是偏向接口型。當函數 setupLocked() 執行之後,就等待這 函數 onSystemReady() 的執行,而 onSystemReady() 由KeyguardService 調用,下面看看 KeyguardService 的啓動。

KeyguardService 啓動

packages/SystemUI/src/com/android/systemui/keyguard/KeyguardService.java 
74      private final IKeyguardService.Stub mBinder = new IKeyguardService.Stub() {
75  
76          @Override // Binder interface
77          public void addStateMonitorCallback(IKeyguardStateCallback callback) {
78              checkPermission();
79              mKeyguardViewMediator.addStateMonitorCallback(callback);
80          }
...
180          @Override // Binder interface
181          public void setKeyguardEnabled(boolean enabled) {
182              checkPermission();
183              mKeyguardViewMediator.setKeyguardEnabled(enabled);
184          }
185  
186          @Override // Binder interface
187          public void onSystemReady() {
188              Trace.beginSection("KeyguardService.mBinder#onSystemReady");
189              checkPermission();
190              mKeyguardViewMediator.onSystemReady();    // -> 調用上一步中KeyguardViewMediator
191              Trace.endSection();
192          }

KeyguardService 是一個service ,其實現了很多的接口,其中 onSystemReady 就是其中之一。

/frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java
7585      /** {@inheritDoc} */
7586      @Override
7587      public void systemReady() {
7588          // In normal flow, systemReady is called before other system services are ready.
7589          // So it is better not to bind keyguard here.
7590          mKeyguardDelegate.onSystemReady();

​ KeyguardService 是被 PhoneWindowManager 綁定了的。PhoneWindowManager 可以直接通過aidl調用KeyguardService 的 onSystemReady 方法。

再次回到 KeyguardViewMediator.java 文件中,此處是執行 onSystemReady 方法

/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java
778      public void onSystemReady() {
779          mHandler.obtainMessage(SYSTEM_READY).sendToTarget();
780      }
...
1611                  case SYSTEM_READY:
1612                      handleSystemReady();
1613                      break;
...
782      private void handleSystemReady() {
783          synchronized (this) {
784              if (DEBUG) Log.d(TAG, "onSystemReady");
785              mSystemReady = true;
786              doKeyguardLocked(null);    // 去準備顯示lock
787              mUpdateMonitor.registerCallback(mUpdateCallback);
788          }
789          // Most services aren't available until the system reaches the ready state, so we
790          // send it here when the device first boots.
791          maybeSendUserPresentBroadcast();
792      }
...
1271      /**
1272       * Enable the keyguard if the settings are appropriate.
1273       */
1274      private void doKeyguardLocked(Bundle options) {
...
1339          if (DEBUG) Log.d(TAG, "doKeyguard: showing the lock screen");
1340          showLocked(options);    // 顯示lock/keyguard 界面  ->  這個裏面其實就是通過handler 發送了一個SHOW的命令,所以直接看函數:handleShow((Bundle) msg.obj);
1341      }
...
1763      private void handleShow(Bundle options) {
...
1777              setShowingLocked(true, mAodShowing);
1778              mStatusBarKeyguardViewManager.show(options);   //  顯示keyguard,則進入到statusbar去了。
1779              mHiding = false;
1780              mWakeAndUnlocking = false;
1781              resetKeyguardDonePendingLocked();
1782              mHideAnimationRun = false;
1783              adjustStatusBarLocked();
1784              userActivity();
1785              mShowKeyguardWakeLock.release();
1786          }
1787          mKeyguardDisplayManager.show();
1789      }

​ 接下來就要處理Keyguard繪製的邏輯了,這部分主要是在StatusBarKeyguardViewManager中。
​ 調用showBouncerOrKeyguard()方法去顯示notification keyguard還是bouncer,在滅屏的情況下,再次亮屏看到的一般是notification keyguard,就是有消息通知、時間之類的那個view,上滑纔會顯示密碼解鎖界面,也就是bouncer。接着就會調用showKeyguard(),當然由於還沒有繪製內容,所以會進行keyguard的繪製。這裏會調用hideBouncer()去隱藏已有的bouncer,因爲下次亮屏的時候可能不是原來的鎖屏方式。例如原來是PIN解鎖,而我們在settings去重置了鎖屏爲pattern,那下次亮屏就應該顯示pattern的view。

/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
187      /**
188       * Show the keyguard.  Will handle creating and attaching to the view manager
189       * lazily.
190       */
191      public void show(Bundle options) {
192          mShowing = true;
193          mStatusBarWindowManager.setKeyguardShowing(true);  //設置一個狀態
194          reset(true /* hideBouncerWhenShowing */);          //重點關注,這裏是重置keyguard,回去判斷需要顯示什麼內容
195          StatsLog.write(StatsLog.KEYGUARD_STATE_CHANGED,
196              StatsLog.KEYGUARD_STATE_CHANGED__STATE__SHOWN);
197      }
...
273      /**
274       * Reset the state of the view.
275       */
276      public void reset(boolean hideBouncerWhenShowing) {
277          if (mShowing) {
278              if (mOccluded && !mDozing) {
279                  mStatusBar.hideKeyguard();
280                  if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) {
281                      hideBouncer(false /* destroyView */);
282                  }
283              } else {
284                  showBouncerOrKeyguard(hideBouncerWhenShowing);
285              }
286              KeyguardUpdateMonitor.getInstance(mContext).sendKeyguardReset();
287              updateStates();
288          }
289      }
....
199      /**
200       * Shows the notification keyguard or the bouncer depending on
201       * {@link KeyguardBouncer#needsFullscreenBouncer()}.
202       */
203      protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) {
204          if (mBouncer.needsFullscreenBouncer() && !mDozing) {
205              // The keyguard might be showing (already). So we need to hide it.
206              mStatusBar.hideKeyguard();
207              mBouncer.show(true /* resetSecuritySelection */);
                  //需要顯示bouncer畫面,也就是輸入密碼、pattern的界面,也可以是本帖子裏面第二個截圖畫面。
208          } else {
209              mStatusBar.showKeyguard();
210              if (hideBouncerWhenShowing) {
211                  hideBouncer(shouldDestroyViewOnReset() /* destroyView */);
212                  mBouncer.prepare();
213              }
214          }
215          updateStates();
216      }

從前面kayguard介紹裏面,我們知道 notification_panel–> R.layout.status_bar_expanded 默認情況下是處於 invisible 狀態的,也就是說,默認情況下該 notification_panel 不會顯示出來,那麼手機啓動之後又是如何啓動的呢?看下面:

805          mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
...
838                      mStatusBarView = (PhoneStatusBarView) fragment.getView();
839                      mStatusBarView.setBar(this);
840                      mStatusBarView.setPanel(mNotificationPanel);   // 重點,此函數爲 PanelBar.java 中的 setPanel
841                      mStatusBarView.setScrimController(mScrimController);
842                      mStatusBarView.setBouncerShowing(mBouncerShowing);   // 在此函數中進行計算是否需要顯示 mNotificationPanel

mNotificationPanel 被設置到 mStatusBarView 裏面去了, 而mNotificationPanel 的顯示與隱藏均是通過 mStatusBarView 調用其內部函數進行控制。

比如:
842                      mStatusBarView.setBouncerShowing(mBouncerShowing);
->
/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
64      public void setPanel(PanelView pv) {
65          mPanel = pv;
66          pv.setBar(this);
67      }
...
69      public void setBouncerShowing(boolean showing) {
70          mBouncerShowing = showing;
71          int important = showing ? IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
72                  : IMPORTANT_FOR_ACCESSIBILITY_AUTO;
73  
74          setImportantForAccessibility(important);
75          updateVisibility();    ----> 
76  
77          if (mPanel != null) mPanel.setImportantForAccessibility(important);
78      }
...
80      private void updateVisibility() {
81          mPanel.setVisibility(mExpanded || mBouncerShowing ? VISIBLE : INVISIBLE);
82      }
// 對 mPanel 進行設置顯示與否。

​ 從這段邏輯,基本上理清楚了 mNotificationPanel 就是我們第一個截屏中顯示的內容,而mNotificationPanel 的控制均在 StatusBar.java 中進行。

​ 如果要在鎖屏界面增加顯示的內容,則也是修改notification_panel–> R.layout.status_bar_expanded即可。

Pattern驗證方式的解鎖流程

從前面的分析,我們已經知道keyguard界面是固化在statusbar裏面的,而bouncer則不是,bouncer需要在屏幕往上滑動纔會觸發顯示出來,所以這裏從鎖屏畫面上劃開始分析。

事件序列

​ 在Android系統中,一個單獨的事件基本上是沒什麼作用的,只有一個事件序列,纔有意義。一個事件序列正常情況下,定義爲 DOWN、MOVE(0或者多個)、UP/CANCEL。事件序列以DOWN事件開始,中間會有0或者多個MOVE事件,最後以UP事件或者CANCEL事件結束。

DOWN事件作爲序列的開始,有一個很重要的職責,就是尋找事件序列的接受者,怎麼理解呢?framework 在DOWN事件的傳遞過程中,需要根據View事件處理方法(onTouchEvent)的返回值來確定事件序列的接受者。如果一個View的onTouchEvent事件,在處理DOWN事件的時候返回true,說明它願意接受並處理該事件序列。

上滑解鎖

​ 當用戶移動手指時,產生touch down事件,最外層view StatusBarWindowView會執行onInterceptTouchEvent,看是否需要攔截touch事件再一級級往子View傳遞,都沒有被攔截,之後執行OnTouchEvent從子View開始一級級往父View傳遞,到PanelView這裏當手指移動的距離達到一定的閾值會調用onTrackingStarted從而設置mTracking的值爲true,onTouchEvent返回true,接收此touch move事件,之後的touch事件直接傳到此View。
​ 在用戶滑動過程會調用setExpandedHeightInternal,進而調用NotificationPanelView的onHeightUpdated進行鎖屏上的時間和通知View根據手指的移動距離進行縮小、變透明處理。
​ 當用戶擡起手指時,產生touch up事件,PanelView接收到這個事件後會調用endMotionEvent,如果手指從down到up之間移動的距離達到一定閾值會調用onTrackingStopped

​ 在上滑過程中,不斷調用PanelView.java的setExpandedHeightInternal()->notifyBarPanelExpansionChanged()–>PanelBar.java的notifyBarPanelExpansionChanged()

也就是前面這一部分都是手指上劃,然後不斷縮小鎖屏界面的高度以及透明度等。

/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java
1189      protected void notifyBarPanelExpansionChanged() {
1190          mBar.panelExpansionChanged(mExpandedFraction, mExpandedFraction > 0f
1191                  || mPeekAnimator != null || mInstantExpanding || isPanelVisibleBecauseOfHeadsUp()
1192                  || mTracking || mHeightAnimator != null);  //調用 PanelBar.java 中的panelExpansionChanged 函數
1193          if (mExpansionListener != null) {
1194              mExpansionListener.accept(mExpandedFraction, mTracking);
1195          }
1196      }
/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java
128      public void panelExpansionChanged(float frac, boolean expanded) {
129          boolean fullyClosed = true;
130          boolean fullyOpened = false;
131          if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
132          PanelView pv = mPanel;
133          mExpanded = expanded;
134          updateVisibility();
135          // adjust any other panels that may be partially visible
136          if (expanded) {
137              if (mState == STATE_CLOSED) {
138                  go(STATE_OPENING);
139                  onPanelPeeked();
140              }
141              fullyClosed = false;
142              final float thisFrac = pv.getExpandedFraction();
143              if (SPEW) LOG("panelExpansionChanged:  -> %s: f=%.1f", pv.getName(), thisFrac);
144              fullyOpened = thisFrac >= 1f;
145          }
146          if (fullyOpened && !mTracking) {
147              go(STATE_OPEN);
148              onPanelFullyOpened();
149          } else if (fullyClosed && !mTracking && mState != STATE_CLOSED) {
150              go(STATE_CLOSED);
151              onPanelCollapsed();  //當到達一定閥值之後會執行這個函數 onPanelCollapsed
152          }
153  
154          if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState,
155                  fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":"");
156      }

上面的這段代碼中主要關注兩個參數

達到閾值時,expanded == false fullyClosed == true 以及 mTracking。 mTracking 在前面說了,當開始滑動的時候就會被設置爲true。而停止滑動之後會變成false。
調用onPanelCollapsed()->調用子類PhoneStatubarView.java的onPanelCollapsed()

/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
194      @Override
195      public void onPanelCollapsed() {
196          super.onPanelCollapsed();
197          // Close the status bar in the next frame so we can show the end of the animation.
198          post(mHideExpandedRunnable);
199          mIsFullyOpenedPanel = false;
200      }
...
63      private Runnable mHideExpandedRunnable = new Runnable() {
64          @Override
65          public void run() {
66              if (mPanelFraction == 0.0f) {
67                  mBar.makeExpandedInvisible();
68              }
69          }
70      };

上面這段代碼最終是執行 mBar.makeExpandedInvisible()

/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
2379      void makeExpandedInvisible() {
2380          if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
2381                  + " mExpandedVisible=" + mExpandedVisible);
2382  
2383          if (!mExpandedVisible || mStatusBarWindow == null) {
2384              return;
2385          }
2386  
2387          // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868)
2388          mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
2389                  1.0f /* speedUpFactor */);
2390  
2391          mNotificationPanel.closeQs();
2392  
2393          mExpandedVisible = false;
2394          visibilityChanged(false);
2395  
2396          // Shrink the window to the size of the status bar only
2397          mStatusBarWindowManager.setPanelVisible(false);
2398          mStatusBarWindowManager.setForceStatusBarVisible(false);
2399  
2400          // Close any guts that might be visible
2401          mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
2402                  true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
2403  
2404          runPostCollapseRunnables();
2405          setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
2406          showBouncerIfKeyguard();   //主要關注這個函數,如果需要驗證則顯示bouncer,如果不需要驗證,則會直接解鎖。
2407          recomputeDisableFlags(mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */);
2408  
2409          // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in
2410          // the bouncer appear animation.
2411          if (!mStatusBarKeyguardViewManager.isShowing()) {
2412              WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN);
2413          }
2414      }
...
4000      private void showBouncerIfKeyguard() {
4001          if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
4002                  && !mKeyguardViewMediator.isHiding()) {
4003              showBouncer(true /* scrimmed */);
4004          }
4005      }
...
4007      protected void showBouncer(boolean scrimmed) {
4008          mStatusBarKeyguardViewManager.showBouncer(scrimmed);
4009      }

mStatusBarWindowManager.setPanelVisible(false);
調用WindowManager更改爲StatusBarWindow的高度, 只保留狀態欄高度
mStatusBarWindowManager.setForceStatusBarVisible(false);
調用WindowManager使狀態欄不可見
showBouncerIfKeyguard()->showBouncer()最終調用到StatusBarKeyguardViewManager的dismiss()->showBouncer()方法

/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
227      public void showBouncer(boolean scrimmed) {
228          if (mShowing && !mBouncer.isShowing()) {
229              mBouncer.show(false /* resetSecuritySelection */, scrimmed);   //調用Bouncer去顯示相關Bouncer
230          }
231          updateStates();
232      }

下面繼續顯示Bouncer或者直接解開鎖屏。

/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
110      /**
111       * Shows the bouncer.
112       *
113       * @param resetSecuritySelection Cleans keyguard view
114       * @param isScrimmed true when the bouncer show show scrimmed, false when the user will be
115       *                 dragging it and translation should be deferred.
116       */
117      public void show(boolean resetSecuritySelection, boolean isScrimmed) {
...
144          final int activeUserId = KeyguardUpdateMonitor.getCurrentUser();
145          final boolean isSystemUser =
146                  UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM;
147          final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId;
148  
149          // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is
150          // set, this will dismiss the whole Keyguard. Otherwise, show the bouncer.
151          if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
152              return;
153          }
//此處會調用 mKeyguardView.dismiss 去嘗試隱藏keyguard,當然在dismiss 的時候,會做一系列的檢查。
/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java
186      /**
187       * Dismisses the keyguard by going to the next screen or making it gone.
188       * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
189       * @return True if the keyguard is done.
190       */
191      public boolean dismiss(int targetUserId) {
192          return dismiss(false, targetUserId);   //注意這裏第一個參數給的是false,表示驗證還沒通過。
193      }
....
207      @Override
208      public boolean dismiss(boolean authenticated, int targetUserId) {
209          return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId);
210      }

/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
312      /**
313       * Shows the next security screen if there is one.
314       * @param authenticated true if the user entered the correct authentication
315       * @param targetUserId a user that needs to be the foreground user at the finish (if called)
316       *     completion.
317       * @return true if keyguard is done
318       */
319      boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId) {
320          if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
321          boolean finish = false;
322          boolean strongAuth = false;
323          if (mUpdateMonitor.getUserCanSkipBouncer(targetUserId)) {
324              finish = true;
325          } else if (SecurityMode.None == mCurrentSecuritySelection) {
326              SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
327              if (SecurityMode.None == securityMode) {
328                  finish = true; // no security required
329              } else {
330                  showSecurityScreen(securityMode); // switch to the alternate security view
331              }
...
360          if (finish) {
361              mSecurityCallback.finish(strongAuth, targetUserId);
362          }
363          return finish;
364      }
//如果我們設有pattern的密碼,則此處會調用  showSecurityScreen 去進行顯示
/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
466      private int getSecurityViewIdForMode(SecurityMode securityMode) {
467          switch (securityMode) {
468              case Pattern: return R.id.keyguard_pattern_view;
469              case PIN: return R.id.keyguard_pin_view;
470              case Password: return R.id.keyguard_password_view;
471              case SimPin: return R.id.keyguard_sim_pin_view;
472              case SimPuk: return R.id.keyguard_sim_puk_view;
473          }
474          return 0;
475      }
...
372      private void showSecurityScreen(SecurityMode securityMode) {
373          if (DEBUG) Log.d(TAG, "showSecurityScreen(" + securityMode + ")");
374  
375          if (securityMode == mCurrentSecuritySelection) return;
376  
377          KeyguardSecurityView oldView = getSecurityView(mCurrentSecuritySelection);
378          KeyguardSecurityView newView = getSecurityView(securityMode);
....
385          if (securityMode != SecurityMode.None) {
386              newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
387              newView.setKeyguardCallback(mCallback);//注意這裏設置了新的callback,在pattern解鎖之後會通過此callback周知信息回來。
388          }
...
389  
390          // Find and show this child.
391          final int childCount = mSecurityViewFlipper.getChildCount();
392  
393          final int securityViewIdForMode = getSecurityViewIdForMode(securityMode);//不同的加密方式,會得到不同的佈局文件,詳情看上面。
394          for (int i = 0; i < childCount; i++) {
395              if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) {
396                  mSecurityViewFlipper.setDisplayedChild(i);
397                  break;
398              }
399          }//顯示出相關解鎖畫面,
400  
401          mCurrentSecuritySelection = securityMode;
402          mSecurityCallback.onSecurityModeChanged(securityMode,
403                  securityMode != SecurityMode.None && newView.needsInput());

​ 上面基本上把需要解鎖的界面顯示出來了,比如pattern 界面是顯示完了。後續當手指在屏幕上滑動的時候,則由相應的 KeyguardPatternView.java 來進行處理。

Pattern的滑動處理:

/packages/SystemUI/src/com/android/keyguard/KeyguardPatternView.java
138      @Override
139      protected void onFinishInflate() {
140          super.onFinishInflate();
141          mLockPatternUtils = mLockPatternUtils == null
142                  ? new LockPatternUtils(mContext) : mLockPatternUtils;
143  
144          mLockPatternView = findViewById(R.id.lockPatternView);
145          mLockPatternView.setSaveEnabled(false);
146          mLockPatternView.setOnPatternListener(new UnlockPatternListener());
147  
// 註冊view的監聽事件。
...
225      private class UnlockPatternListener implements LockPatternView.OnPatternListener {
226  
227          @Override
228          public void onPatternStart() {
229              mLockPatternView.removeCallbacks(mCancelPatternRunnable);
230              mSecurityMessageDisplay.setMessage("");
231          }
232  
233          @Override
234          public void onPatternCleared() {
235          }
236  
237          @Override
238          public void onPatternCellAdded(List<LockPatternView.Cell> pattern) {
239              mCallback.userActivity();
240          }
241  
242          @Override
243          public void onPatternDetected(final List<LockPatternView.Cell> pattern) {
244              mLockPatternView.disableInput();
245              if (mPendingLockCheck != null) {
246                  mPendingLockCheck.cancel(false);
247              }
248  
249              final int userId = KeyguardUpdateMonitor.getCurrentUser();
250              if (pattern.size() < LockPatternUtils.MIN_PATTERN_REGISTER_FAIL) {
251                  mLockPatternView.enableInput();
252                  onPatternChecked(userId, false, 0, false /* not valid - too short */);
253                  return;
254              }
255  
256              if (LatencyTracker.isEnabled(mContext)) {
257                  LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL);
258                  LatencyTracker.getInstance(mContext).onActionStart(ACTION_CHECK_CREDENTIAL_UNLOCKED);
259              }
260              mPendingLockCheck = LockPatternChecker.checkPattern(
261                      mLockPatternUtils,
262                      pattern,
263                      userId,
264                      new LockPatternChecker.OnCheckCallback() {
265  
266                          @Override
267                          public void onEarlyMatched() {
268                              if (LatencyTracker.isEnabled(mContext)) {
269                                  LatencyTracker.getInstance(mContext).onActionEnd(
270                                          ACTION_CHECK_CREDENTIAL);
271                              }
272                              onPatternChecked(userId, true /* matched */, 0 /* timeoutMs */,
273                                      true /* isValidPattern */);
274                          }
275  
276                          @Override
277                          public void onChecked(boolean matched, int timeoutMs) {
278                              if (LatencyTracker.isEnabled(mContext)) {
279                                  LatencyTracker.getInstance(mContext).onActionEnd(
280                                          ACTION_CHECK_CREDENTIAL_UNLOCKED);
281                              }
282                              mLockPatternView.enableInput();
283                              mPendingLockCheck = null;
284                              if (!matched) {
285                                  onPatternChecked(userId, false /* matched */, timeoutMs,
286                                          true /* isValidPattern */);
287                              }
288                          }
289  
290                          @Override
291                          public void onCancelled() {
292                              // We already got dismissed with the early matched callback, so we
293                              // cancelled the check. However, we still need to note down the latency.
294                              if (LatencyTracker.isEnabled(mContext)) {
295                                  LatencyTracker.getInstance(mContext).onActionEnd(
296                                          ACTION_CHECK_CREDENTIAL_UNLOCKED);
297                              }
298                          }
299                      });
300              if (pattern.size() > MIN_PATTERN_BEFORE_POKE_WAKELOCK) {
301                  mCallback.userActivity();
302              }
303          }
UnlockPatternListener 這個listerner完整代碼如上,此處主要實現了幾個接口:
onPatternStart  開始劃
onPatternCleared 
onPatternCellAdded
onPatternDetected

以及在detect到密碼之後調用  mPendingLockCheck = LockPatternChecker.checkPattern 去做最終的驗證。這塊調用framework去做驗證的步驟就不在看了,總之就是先把pattern轉換成string,然後和既有密碼進行匹配。
從  LockPatternChecker.checkPattern 裏面看,傳如的callback -> OnCheckCallback ,可以知道當pattern檢查有結果之後會調用:onPatternChecked , 其第二個參數表示密碼是否匹配。


305          private void onPatternChecked(int userId, boolean matched, int timeoutMs,
306                  boolean isValidPattern) {
307              boolean dismissKeyguard = KeyguardUpdateMonitor.getCurrentUser() == userId;
308              if (matched) { //密碼驗證正確
309                  mCallback.reportUnlockAttempt(userId, true, 0);
310                  if (dismissKeyguard) {
311                      mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Correct);
312                      mCallback.dismiss(true, userId); //密碼驗證成功,調用回調隱藏keyguard
313                  }
314              } else {   //密碼驗證失敗。
315                  mLockPatternView.setDisplayMode(LockPatternView.DisplayMode.Wrong);
316                  if (isValidPattern) {
317                      mCallback.reportUnlockAttempt(userId, false, timeoutMs);
318                      if (timeoutMs > 0) {
319                          long deadline = mLockPatternUtils.setLockoutAttemptDeadline(
320                                  userId, timeoutMs);
321                          handleAttemptLockout(deadline);
322                      }
323                  }
324                  if (timeoutMs == 0) {
325                      mSecurityMessageDisplay.setMessage(R.string.kg_wrong_pattern);
326                      mLockPatternView.postDelayed(mCancelPatternRunnable, PATTERN_CLEAR_TIMEOUT_MS);
327                  }
328              }
329          }
330      }
此處的 mCallback 是前面 showSecurityScreen() 函數裏面的如下代碼:
385          if (securityMode != SecurityMode.None) {
386              newView.onResume(KeyguardSecurityView.VIEW_REVEALED);
387              newView.setKeyguardCallback(mCallback);
388          }

​ 總結一下:在繪製密碼後手指擡起的時候,如果已存的有效點數達到4個及以上,就會使用LockPatternChecker.checkPattern方法啓動一個AsyncTask, 並在doInBackground中調用LockPatternUtils.checkPattern進行密碼驗證,此時pattern會被轉化成字符串形式,最終和密碼鎖PIN碼鎖一樣,都是遠程調用到LockPatternService的checkCredential接口進行驗證。在整個操作過程中,mOnPatternListener被用於通知LockPatternView進行安全鎖提示內容和Pattern狀態的刷新。

​ checkCredential的具體算法邏輯以及LockPatternUtils和LockSettingsService中還有很多與安全鎖加鎖/解鎖/鎖狀態判斷相關的接口,後面我們會單獨進行分析:)。

問題4:指紋解鎖流程?

這個問題將在下一篇中進行闡述。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章