咱們理解Keyguard的解鎖流程主要從鎖屏的界面Layout結構、touchEvent事件分發、解鎖動做邏輯幾個方面進行源碼的分析html
整個鎖屏界面的頂級View就是mStatusBarWindow
src/com/android/systemui/statusbar/phone/StatusBar.java
java
public void createAndAddWindows() { addStatusBarWindow(); } private void addStatusBarWindow() { makeStatusBarView(); mStatusBarWindowManager = Dependency.get(StatusBarWindowManager.class); mRemoteInputController = new RemoteInputController(mHeadsUpManager); mStatusBarWindowManager.add(mStatusBarWindow, getStatusBarHeight()); // add by hai.qin for story lock if (mLockScreenManager != null) { mLockScreenManager.setStatusBarWindowManager(mStatusBarWindowManager); } // }
src/com/android/systemui/statusbar/phone/StatusBarWindowManager.java
android
/** * Adds the status bar view to the window manager. * * @param statusBarView The view to add. * @param barHeight The height of the status bar in collapsed state. */ public void add(View statusBarView, int barHeight) { // Now that the status bar window encompasses the sliding panel and its // translucent backdrop, the entire thing is made TRANSLUCENT and is // hardware-accelerated. mLp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, barHeight, WindowManager.LayoutParams.TYPE_STATUS_BAR, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH | WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, PixelFormat.TRANSLUCENT); mLp.token = new Binder(); mLp.gravity = Gravity.TOP; mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE; mLp.setTitle("StatusBar"); mLp.packageName = mContext.getPackageName(); mStatusBarView = statusBarView; mBarHeight = barHeight; mWindowManager.addView(mStatusBarView, mLp); mLpChanged = new WindowManager.LayoutParams(); mLpChanged.copyFrom(mLp); }
mStatusBarWindow是在StatusBar的create流程中調用WindowManager.addView()添加到窗口上的, type爲WindowManager.LayoutParams.TYPE_STATUS_BAR安全
鎖屏界面的Layout結構能夠簡單歸納爲如下結構:
mStatusBarWindow--> R.layout.super_status_bar
notification_panel--> R.layout.status_bar_expanded
keyguardBouncer-->R.layout.keyguard_bouncer
app
mStatusBarWindow-->notification_panel-->notification_container_parent-->keyguard_header(鎖屏狀態欄) | | | -->keyguard_bottom_area (lock_icon和充電狀態等) | | | -->keyguard_status_view (鎖屏時鐘日期) | | | -->keyguard_up_slide (箭頭提示動畫) | -->keyguardBouncer(安全鎖界面)
上劃後顯示的安全鎖界面是KeyguardBouncer,但keyguardbouncer並無寫在super_status_bar的layout文件裏面,那麼他是在何時添加的呢?ide
src/com/android/systemui/statusbar/phone/StatusBar.java
-->start()-->startKeyguard()post
protected void startKeyguard() { ... mStatusBarKeyguardViewManager = keyguardViewMediator.registerStatusBar(this, getBouncerContainer(), mScrimController, mFingerprintUnlockController); ... }
src/com/android/systemui/keyguard/KeyguardViewMediator.java
-->registerStatusBar()
src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
-->registerStatusBar()動畫
public void registerStatusBar(StatusBar statusBar, ViewGroup container, ScrimController scrimController, FingerprintUnlockController fingerprintUnlockController, DismissCallbackRegistry dismissCallbackRegistry) { mStatusBar = statusBar; mContainer = container; mScrimController = scrimController; mFingerprintUnlockController = fingerprintUnlockController; mBouncer = SystemUIFactory.getInstance().createKeyguardBouncer(mContext, mViewMediatorCallback, mLockPatternUtils, container, dismissCallbackRegistry); }
protected ViewGroup getBouncerContainer() { return mStatusBarWindow; }
src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
-->show()-->ensureView()-->inflateView()ui
protected void inflateView() { removeView(); mHandler.removeCallbacks(mRemoveViewRunnable); mRoot = (ViewGroup) LayoutInflater.from(mContext).inflate(R.layout.keyguard_bouncer, null); mKeyguardView = mRoot.findViewById(R.id.keyguard_host_view); mKeyguardView.setLockPatternUtils(mLockPatternUtils); mKeyguardView.setViewMediatorCallback(mCallback); mContainer.addView(mRoot, mContainer.getChildCount()); mRoot.setVisibility(View.INVISIBLE); final WindowInsets rootInsets = mRoot.getRootWindowInsets(); if (rootInsets != null) { mRoot.dispatchApplyWindowInsets(rootInsets); } }
src/com/android/keyguard/KeyguardSecurityContainer.javathis
private KeyguardSecurityView getSecurityView(SecurityMode securityMode) { final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); KeyguardSecurityView view = null; final int children = mSecurityViewFlipper.getChildCount(); for (int child = 0; child < children; child++) { if (mSecurityViewFlipper.getChildAt(child).getId() == securityViewIdForMode) { view = ((KeyguardSecurityView)mSecurityViewFlipper.getChildAt(child)); break; } } int layoutId = getLayoutIdFor(securityMode); if (view == null && layoutId != 0) { final LayoutInflater inflater = LayoutInflater.from(mContext); if (DEBUG) Log.v(TAG, "inflating id = " + layoutId); View v = inflater.inflate(layoutId, mSecurityViewFlipper, false); mSecurityViewFlipper.addView(v); updateSecurityView(v); view = (KeyguardSecurityView)v; } return view; }
每次獲取securityview的時候 先判斷是否在viewflippter裏存在該id
沒有的話 inflate一個新的同時addview到viewflippter裏面
在顯示的時候調用showSecurityScreen(SecurityMode securityMode)
private void showSecurityScreen(SecurityMode securityMode) { ... // Find and show this child. final int childCount = mSecurityViewFlipper.getChildCount(); final int securityViewIdForMode = getSecurityViewIdForMode(securityMode); for (int i = 0; i < childCount; i++) { if (mSecurityViewFlipper.getChildAt(i).getId() == securityViewIdForMode) { mSecurityViewFlipper.setDisplayedChild(i); break; } } ... }
會根據securitymode選擇viewflipper中對應的child進行顯示
咱們這裏分析上滑解鎖過程當中的touchEvent事件分發
讓咱們來先回顧一個android中的事件分發概念:事件序列
在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()
src/com/android/systemui/statusbar/phone/PanelBar.java
public void panelExpansionChanged(float frac, boolean expanded) { Log.d("WANG", "panelExpansionChanged frac=" + frac + " expaned=" + expanded ); boolean fullyClosed = true; boolean fullyOpened = false; if (SPEW) LOG("panelExpansionChanged: start state=%d", mState); PanelView pv = mPanel; pv.setVisibility(expanded ? VISIBLE : INVISIBLE); // adjust any other panels that may be partially visible if (expanded) { if (mState == STATE_CLOSED) { go(STATE_OPENING); onPanelPeeked(); } fullyClosed = false; final float thisFrac = pv.getExpandedFraction(); if (SPEW) LOG("panelExpansionChanged: -> %s: f=%.1f", pv.getName(), thisFrac); fullyOpened = thisFrac >= 1f; } if (fullyOpened && !mTracking) { go(STATE_OPEN); onPanelFullyOpened(); } else if (fullyClosed && !mTracking && mState != STATE_CLOSED) { go(STATE_CLOSED); onPanelCollapsed(); } if (SPEW) LOG("panelExpansionChanged: end state=%d [%s%s ]", mState, fullyOpened?" fullyOpened":"", fullyClosed?" fullyClosed":""); }
達到閾值時,expanded == false fullyClosed == true
調用onPanelCollapsed()->調用子類PhoneStatubarView.java的onPanelCollapsed()
src/com/android/systemui/statusbar/phone/PhoneStatusBarView.java
@Override public void onPanelCollapsed() { super.onPanelCollapsed(); // Close the status bar in the next frame so we can show the end of the animation. post(mHideExpandedRunnable); mIsFullyOpenedPanel = false; } private Runnable mHideExpandedRunnable = new Runnable() { @Override public void run() { if (mPanelFraction == 0.0f) { mBar.makeExpandedInvisible(); } } };
整個解鎖過程分爲兩個部分:1. 隱藏notification_panel 2. 展現keyguard_bouncer或直接解鎖
src/com/android/systemui/statusbar/phone/StatusBar.java
void makeExpandedInvisible() { if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible + " mExpandedVisible=" + mExpandedVisible); if (!mExpandedVisible || mStatusBarWindow == null) { return; } // Ensure the panel is fully collapsed (just in case; bug 6765842, 7260868) mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/, 1.0f /* speedUpFactor */); mNotificationPanel.closeQs(); mExpandedVisible = false; visibilityChanged(false); // Shrink the window to the size of the status bar only mStatusBarWindowManager.setPanelVisible(false); mStatusBarWindowManager.setForceStatusBarVisible(false); // Close any guts that might be visible closeAndSaveGuts(true /* removeLeavebehind */, true /* force */, true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */); runPostCollapseRunnables(); setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false); showBouncerIfKeyguard(); recomputeDisableFlags(mNotificationPanel.hideStatusBarIconsWhenExpanded() /* animate */); // Trimming will happen later if Keyguard is showing - doing it here might cause a jank in // the bouncer appear animation. if (!mStatusBarKeyguardViewManager.isShowing()) { WindowManagerGlobal.getInstance().trimMemory(ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN); } }
mStatusBarWindowManager.setPanelVisible(false);
調用WindowManager更改成StatusBarWindow的高度, 只保留狀態欄高度
mStatusBarWindowManager.setForceStatusBarVisible(false);
調用WindowManager使狀態欄不可見
showBouncerIfKeyguard()->showBouncer()最終調用到StatusBarKeyguardViewManager的dismiss()->showBouncer()方法
src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
private void showBouncer() { if (mShowing) { mBouncer.show(false /* resetSecuritySelection */); } updateStates(); }
src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
public void show(boolean resetSecuritySelection) { ... // If allowed, try to dismiss the Keyguard. If no security auth (password/pin/pattern) is // set, this will dismiss the whole Keyguard. Otherwise, show the bouncer. if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) { return; } ... }
在沒有安全鎖的狀況下,會回調KeyguardHostView的finish方法
@Override public void finish(boolean strongAuth, int targetUserId) { // If there's a pending runnable because the user interacted with a widget // and we're leaving keyguard, then run it. boolean deferKeyguardDone = false; if (mDismissAction != null) { deferKeyguardDone = mDismissAction.onDismiss(); mDismissAction = null; mCancelAction = null; } if (mViewMediatorCallback != null) { if (deferKeyguardDone) { mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId); } else { mViewMediatorCallback.keyguardDone(strongAuth, targetUserId); } } }
mViewMediatorCallback定義在KeyguardViewMediator中
@Override public void keyguardDone(boolean strongAuth, int targetUserId) { if (targetUserId != ActivityManager.getCurrentUser()) { return; } tryKeyguardDone(); }
一系列調用來到解鎖的核心代碼
mKeyguardGoingAwayRunnable.run();
private final Runnable mKeyguardGoingAwayRunnable = new Runnable() { @Override public void run() { Trace.beginSection("KeyguardViewMediator.mKeyGuardGoingAwayRunnable"); if (DEBUG) Log.d(TAG, "keyguardGoingAway"); //Modified for MYOS begin try { mStatusBarKeyguardViewManager.keyguardGoingAway(); int flags = 0; if (mStatusBarKeyguardViewManager.shouldDisableWindowAnimationsForUnlock() || mWakeAndUnlocking || mAniSpeedup) { flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_NO_WINDOW_ANIMATIONS; } if (mStatusBarKeyguardViewManager.isGoingToNotificationShade()) { flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_TO_SHADE; } if (mStatusBarKeyguardViewManager.isUnlockWithWallpaper()) { flags |= WindowManagerPolicy.KEYGUARD_GOING_AWAY_FLAG_WITH_WALLPAPER; } mUpdateMonitor.setKeyguardGoingAway(true /* goingAway */); // Don't actually hide the Keyguard at the moment, wait for window // manager until it tells us it's safe to do so with // startKeyguardExitAnimation. ActivityManager.getService().keyguardGoingAway(flags); } catch (RemoteException e) { Log.e(TAG, "Error while calling WindowManager", e); } //Modified for MYOS end Trace.endSection(); } };
解鎖過程的核心實質上是鎖屏啓動了一個runnable,
通知AMS和WMS顯示鎖屏下方的activity組件窗口以及調用該activity組件的生命週期
void keyguardGoingAway(int flags) { if (!mKeyguardShowing) { return; } Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway"); mWindowManager.deferSurfaceLayout(); try { setKeyguardGoingAway(true); mWindowManager.prepareAppTransition(TRANSIT_KEYGUARD_GOING_AWAY, false /* alwaysKeepCurrent */, convertTransitFlags(flags), false /* forceOverride */); updateKeyguardSleepToken(); // Some stack visibility might change (e.g. docked stack) mStackSupervisor.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS); mStackSupervisor.addStartingWindowsForVisibleActivities(true /* taskSwitch */); mWindowManager.executeAppTransition(); } finally { Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "keyguardGoingAway: surfaceLayout"); mWindowManager.continueSurfaceLayout(); Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER); } }
在系統準備解鎖完成後,PhoneWindowManager回調KeyguardService的startKeyguardExitAnimation
private void handleStartKeyguardExitAnimation(long startTime, long fadeoutDuration) { Trace.beginSection("KeyguardViewMediator#handleStartKeyguardExitAnimation"); if (DEBUG) Log.d(TAG, "handleStartKeyguardExitAnimation startTime=" + startTime + " fadeoutDuration=" + fadeoutDuration); synchronized (KeyguardViewMediator.this) { if (!mHiding) { return; } mHiding = false; if (mWakeAndUnlocking && mDrawnCallback != null) { // Hack level over 9000: To speed up wake-and-unlock sequence, force it to report // the next draw from here so we don't have to wait for window manager to signal // this to our ViewRootImpl. mStatusBarKeyguardViewManager.getViewRootImpl().setReportNextDraw(); notifyDrawn(mDrawnCallback); mDrawnCallback = null; } // only play "unlock" noises if not on a call (since the incall UI // disables the keyguard) if (TelephonyManager.EXTRA_STATE_IDLE.equals(mPhoneState)) { playSounds(false); } mWakeAndUnlocking = false; setShowingLocked(false); mDismissCallbackRegistry.notifyDismissSucceeded(); mStatusBarKeyguardViewManager.hide(startTime, fadeoutDuration); resetKeyguardDonePendingLocked(); mHideAnimationRun = false; adjustStatusBarLocked(); sendUserPresentBroadcast(); mUpdateMonitor.setKeyguardGoingAway(false /* goingAway */); // ADD FOR FINGERPRINT SHOT BEGIN mFingerPrintManager.notifyFpService(1, null); // ADD FOR FINGERPRINT SHOT END } Trace.endSection(); }
播放解鎖聲音、設置StatusBar的flag、發出ACTION_USER_PRESENT廣播、隱藏KeyguardView,解鎖流程結束