我們理解Keyguard的解鎖流程主要從鎖屏的界面Layout結(jié)構(gòu)缀拭、touchEvent事件分發(fā)、解鎖動(dòng)作邏輯幾個(gè)方面進(jìn)行源碼的分析
鎖屏的界面Layout結(jié)構(gòu)分析
StatusbarWindowView
整個(gè)鎖屏界面的頂級(jí)View就是mStatusBarWindow
src/com/android/systemui/statusbar/phone/StatusBar.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
/**
* 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流程中調(diào)用WindowManager.addView()添加到窗口上的田巴, type為WindowManager.LayoutParams.TYPE_STATUS_BAR
Layout結(jié)構(gòu)
鎖屏界面的Layout結(jié)構(gòu)可以簡(jiǎn)單概括為以下結(jié)構(gòu):
mStatusBarWindow--> R.layout.super_status_bar
notification_panel--> R.layout.status_bar_expanded
keyguardBouncer-->R.layout.keyguard_bouncer
mStatusBarWindow-->notification_panel-->notification_container_parent-->keyguard_header(鎖屏狀態(tài)欄)
| |
| -->keyguard_bottom_area (lock_icon和充電狀態(tài)等)
| |
| -->keyguard_status_view (鎖屏?xí)r鐘日期)
| |
| -->keyguard_up_slide (箭頭提示動(dòng)畫)
|
-->keyguardBouncer(安全鎖界面)
上劃后顯示的安全鎖界面是KeyguardBouncer超歌,但keyguardbouncer并沒有寫在super_status_bar的layout文件里面专普,那么他是在什么時(shí)候添加的呢?
KeyguarBouncer何時(shí)創(chuàng)建
src/com/android/systemui/statusbar/phone/StatusBar.java
-->start()-->startKeyguard()
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);
}
那么這里的container是什么码耐?
protected ViewGroup getBouncerContainer() {
return mStatusBarWindow;
}
是什么時(shí)候把keyguard_host_view加入到mStatuBarWindow的?
src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
-->show()-->ensureView()-->inflateView()
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);
}
}
不同類型的安全鎖是怎么放入keyguard_host_view的追迟?
src/com/android/keyguard/KeyguardSecurityContainer.java
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的時(shí)候 先判斷是否在viewflippter里存在該id
沒有的話 inflate一個(gè)新的同時(shí)addview到viewflippter里面
在顯示的時(shí)候調(diào)用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;
}
}
...
}
會(huì)根據(jù)securitymode選擇viewflipper中對(duì)應(yīng)的child進(jìn)行顯示
touchEvent事件分發(fā)
我們這里分析上滑解鎖過程中的touchEvent事件分發(fā)
讓我們來先回顧一個(gè)android中的事件分發(fā)概念:事件序列
事件序列
在Android系統(tǒng)中,一個(gè)單獨(dú)的事件基本上是沒什么作用的骚腥,只有一個(gè)事件序列敦间,才有意義。一個(gè)事件序列正常情況下束铭,定義為 DOWN每瞒、MOVE(0或者多個(gè))、UP/CANCEL纯露。事件序列以DOWN事件開始,中間會(huì)有0或者多個(gè)MOVE事件代芜,最后以UP事件或者CANCEL事件結(jié)束埠褪。
DOWN事件作為序列的開始,有一個(gè)很重要的職責(zé)挤庇,就是尋找事件序列的接受者钞速,怎么理解呢?framework 在DOWN事件的傳遞過程中嫡秕,需要根據(jù)View事件處理方法(onTouchEvent)的返回值來確定事件序列的接受者渴语。如果一個(gè)View的onTouchEvent事件,在處理DOWN事件的時(shí)候返回true昆咽,說明它愿意接受并處理該事件序列驾凶。
上滑解鎖
當(dāng)用戶移動(dòng)手指時(shí),產(chǎn)生touch down事件掷酗,
最外層view StatusBarWindowView會(huì)執(zhí)行onInterceptTouchEvent调违,看是否需要攔截touch事件
再一級(jí)級(jí)往子View傳遞,都沒有被攔截泻轰,之后執(zhí)行OnTouchEvent從子View開始一級(jí)級(jí)往父View傳遞技肩,到PanelView這里當(dāng)手指移動(dòng)的距離達(dá)到一定的閾值會(huì)調(diào)用onTrackingStarted從而設(shè)置mTracking的值為true,onTouchEvent返回true浮声,接收此touch move事件虚婿,之后的touch事件直接傳到此View。
在用戶滑動(dòng)過程會(huì)調(diào)用setExpandedHeightInternal,進(jìn)而調(diào)用NotificationPanelView的onHeightUpdated進(jìn)行鎖屏上的時(shí)間和通知View根據(jù)手指的移動(dòng)距離進(jìn)行縮小泳挥、變透明處理然痊。
當(dāng)用戶抬起手指時(shí),產(chǎn)生touch up事件羡洁,PanelView接收到這個(gè)事件后會(huì)調(diào)用endMotionEvent玷过,如果手指從down到up之間移動(dòng)的距離達(dá)到一定閾值會(huì)調(diào)用onTrackingStopped
在上滑過程中,不斷調(diào)用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":"");
}
達(dá)到閾值時(shí),expanded == false fullyClosed == true
調(diào)用onPanelCollapsed()->調(diào)用子類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();
}
}
};
解鎖動(dòng)作邏輯
整個(gè)解鎖過程分為兩個(gè)部分: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);
調(diào)用WindowManager更改為StatusBarWindow的高度辛蚊, 只保留狀態(tài)欄高度
mStatusBarWindowManager.setForceStatusBarVisible(false);
調(diào)用WindowManager使?fàn)顟B(tài)欄不可見
showBouncerIfKeyguard()->showBouncer()最終調(diào)用到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;
}
...
}
在沒有安全鎖的情況下粤蝎,會(huì)回調(diào)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();
}
一系列調(diào)用來到解鎖的核心代碼
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();
}
};
解鎖過程的核心實(shí)質(zhì)上是鎖屏啟動(dòng)了一個(gè)runnable,
通知AMS和WMS顯示鎖屏下方的activity組件窗口以及調(diào)用該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);
}
}
在系統(tǒng)準(zhǔn)備解鎖完成后袋马,PhoneWindowManager回調(diào)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();
}
播放解鎖聲音初澎、設(shè)置StatusBar的flag、發(fā)出ACTION_USER_PRESENT廣播虑凛、隱藏KeyguardView碑宴,解鎖流程結(jié)束