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:指紋解鎖流程?
這個問題將在下一篇中進行闡述。