Android 10.0 滑动解锁流程
2023-12-30 / 龙之叶   

前言

滑动解锁相对于来说逻辑还是简单的,说白了就是对事件的处理,然后做一些事。
这里主要从锁屏的界面Layout结构、touchEvent事件分发、解锁动作逻辑几个方面进行源码的分析。

锁屏的界面Layout结构分析

StatusbarWindowView
整个锁屏界面的顶级 View 就是 StatusbarWindowView;
StatusBar#createAndAddWindows()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java

1
2
3
4
5
6
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {
makeStatusBarView(result);
mNotificationShadeWindowController.attach();
// 添加视图
mStatusBarWindowController.attach();
}

StatusBarWindowController#attach()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarWindowController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public void attach() {
// 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,
mBarHeight,
WindowManager.LayoutParams.TYPE_STATUS_BAR,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,
PixelFormat.TRANSLUCENT);
mLp.privateFlags |= PRIVATE_FLAG_COLOR_SPACE_AGNOSTIC;
mLp.token = new Binder();
mLp.gravity = Gravity.TOP;
mLp.setFitInsetsTypes(0 /* types */);
mLp.setTitle("StatusBar");
mLp.packageName = mContext.getPackageName();
mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
mWindowManager.addView(mStatusBarView, mLp);
mLpChanged.copyFrom(mLp);
}

StatusBarWindow 是在 StatusBar 的 createAndAddWindows() 流程中调用StatusBarWindowController.attach() 添加到窗口上的, type为WindowManager.LayoutParams.TYPE_STATUS_BAR

Layout结构

锁屏界面的Layout结构可以简单概括为以下结构:

  • mStatusBarWindow–> R.layout.super_status_bar
  • notification_panel–> R.layout.status_bar_expanded
  • keyguardBouncer–>R.layout.keyguard_bouncer
    1
    2
    3
    4
    5
    6
    7
    8
    9
    mStatusBarWindow-->notification_panel-->notification_container_parent-->keyguard_header(锁屏状态栏)
    | |
    | -->keyguard_bottom_area (lock_icon和充电状态等)
    | |
    | -->keyguard_status_view (锁屏时钟日期)
    | |
    | -->keyguard_up_slide (箭头提示动画)
    |
    -->keyguardBouncer(安全锁界面)

touchEvent事件分发

我们这里分析上滑解锁过程中的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。

  1. 硬件发出指令:按下,移动,抬起
  2. input接收
  3. 代码执行相应操作:ACTION_DOWN,ACTION_MOVE,ACTION_UP

PanelView#onInterceptTouchEvent()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelView.java

1
2
3
4
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
return mTouchHandler.onInterceptTouchEvent(event);
}

PanelViewController
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
public class TouchHandler implements View.OnTouchListener {
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
&& event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
return false;
}

/*
* If the user drags anywhere inside the panel we intercept it if the movement is
* upwards. This allows closing the shade from anywhere inside the panel.
*
* We only do this if the current content is scrolled to the bottom,
* i.e canCollapsePanelOnTouch() is true and therefore there is no conflicting scrolling
* gesture
* possible.
*/
int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);
boolean canCollapsePanel = canCollapsePanelOnTouch();

switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mStatusBar.userActivity();
mAnimatingOnDown = mHeightAnimator != null;
mMinExpandHeight = 0.0f;
mDownTime = SystemClock.uptimeMillis();
if (mAnimatingOnDown && mClosing && !mHintAnimationRunning
|| mPeekAnimator != null) {
cancelHeightAnimator();
cancelPeek();
mTouchSlopExceeded = true;
return true;
}
mInitialTouchY = y;
mInitialTouchX = x;
mTouchStartedInEmptyArea = !isInContentBounds(x, y);
mTouchSlopExceeded = mTouchSlopExceededBeforeDown;
mJustPeeked = false;
mMotionAborted = false;
mPanelClosedOnDown = isFullyCollapsed();
mCollapsedAndHeadsUpOnDown = false;
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mTouchAboveFalsingThreshold = false;
addMovement(event);
break;
case MotionEvent.ACTION_POINTER_UP:
final int upPointer = event.getPointerId(event.getActionIndex());
if (mTrackingPointer == upPointer) {
// gesture is ongoing, find a new pointer to track
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
mTrackingPointer = event.getPointerId(newIndex);
mInitialTouchX = event.getX(newIndex);
mInitialTouchY = event.getY(newIndex);
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mMotionAborted = true;
mVelocityTracker.clear();
}
break;
case MotionEvent.ACTION_MOVE:
final float h = y - mInitialTouchY;
addMovement(event);
if (canCollapsePanel || mTouchStartedInEmptyArea || mAnimatingOnDown) {
float hAbs = Math.abs(h);
float touchSlop = getTouchSlop(event);
if ((h < -touchSlop || (mAnimatingOnDown && hAbs > touchSlop))
&& hAbs > Math.abs(x - mInitialTouchX)) {
cancelHeightAnimator();
startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
return true;
}
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mVelocityTracker.clear();
break;
}
return false;
}

@Override
public boolean onTouch(View v, MotionEvent event) {
if (mInstantExpanding || (mTouchDisabled
&& event.getActionMasked() != MotionEvent.ACTION_CANCEL) || (mMotionAborted
&& event.getActionMasked() != MotionEvent.ACTION_DOWN)) {
return false;
}

// If dragging should not expand the notifications shade, then return false.
if (!mNotificationsDragEnabled) {
if (mTracking) {
// Turn off tracking if it's on or the shade can get stuck in the down position.
onTrackingStopped(true /* expand */);
}
return false;
}

// On expanding, single mouse click expands the panel instead of dragging.
if (isFullyCollapsed() && event.isFromSource(InputDevice.SOURCE_MOUSE)) {
if (event.getAction() == MotionEvent.ACTION_UP) {
expand(true);
}
return true;
}

/*
* We capture touch events here and update the expand height here in case according to
* the users fingers. This also handles multi-touch.
*
* If the user just clicks shortly, we show a quick peek of the shade.
*
* Flinging is also enabled in order to open or close the shade.
*/

int pointerIndex = event.findPointerIndex(mTrackingPointer);
if (pointerIndex < 0) {
pointerIndex = 0;
mTrackingPointer = event.getPointerId(pointerIndex);
}
final float x = event.getX(pointerIndex);
final float y = event.getY(pointerIndex);

if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mGestureWaitForTouchSlop = shouldGestureWaitForTouchSlop();
mIgnoreXTouchSlop = isFullyCollapsed() || shouldGestureIgnoreXTouchSlop(x, y);
}

switch (event.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
mJustPeeked = false;
mMinExpandHeight = 0.0f;
mPanelClosedOnDown = isFullyCollapsed();
mHasLayoutedSinceDown = false;
mUpdateFlingOnLayout = false;
mMotionAborted = false;
mPeekTouching = mPanelClosedOnDown;
mDownTime = SystemClock.uptimeMillis();
mTouchAboveFalsingThreshold = false;
mCollapsedAndHeadsUpOnDown =
isFullyCollapsed() && mHeadsUpManager.hasPinnedHeadsUp();
addMovement(event);
if (!mGestureWaitForTouchSlop || (mHeightAnimator != null
&& !mHintAnimationRunning) || mPeekAnimator != null) {
mTouchSlopExceeded =
(mHeightAnimator != null && !mHintAnimationRunning)
|| mPeekAnimator != null || mTouchSlopExceededBeforeDown;
cancelHeightAnimator();
cancelPeek();
onTrackingStarted();
}
if (isFullyCollapsed() && !mHeadsUpManager.hasPinnedHeadsUp()
&& !mStatusBar.isBouncerShowing()) {
startOpening(event);
}
break;

case MotionEvent.ACTION_POINTER_UP:
final int upPointer = event.getPointerId(event.getActionIndex());
if (mTrackingPointer == upPointer) {
// gesture is ongoing, find a new pointer to track
final int newIndex = event.getPointerId(0) != upPointer ? 0 : 1;
final float newY = event.getY(newIndex);
final float newX = event.getX(newIndex);
mTrackingPointer = event.getPointerId(newIndex);
startExpandMotion(newX, newY, true /* startTracking */, mExpandedHeight);
}
break;
case MotionEvent.ACTION_POINTER_DOWN:
if (mStatusBarStateController.getState() == StatusBarState.KEYGUARD) {
mMotionAborted = true;
endMotionEvent(event, x, y, true /* forceCancel */);
return false;
}
break;
case MotionEvent.ACTION_MOVE:
addMovement(event);
float h = y - mInitialTouchY;

// If the panel was collapsed when touching, we only need to check for the
// y-component of the gesture, as we have no conflicting horizontal gesture.
if (Math.abs(h) > getTouchSlop(event)
&& (Math.abs(h) > Math.abs(x - mInitialTouchX)
|| mIgnoreXTouchSlop)) {
mTouchSlopExceeded = true;
if (mGestureWaitForTouchSlop && !mTracking && !mCollapsedAndHeadsUpOnDown) {
if (!mJustPeeked && mInitialOffsetOnTouch != 0f) {
startExpandMotion(x, y, false /* startTracking */, mExpandedHeight);
h = 0;
}
cancelHeightAnimator();
// 向上滑动时,手指移动的距离达到一定的阈值会调用onTrackingStarted,
// 设置mTracking值为true,从而接收touch事件
onTrackingStarted();
}
}
float newHeight = Math.max(0, h + mInitialOffsetOnTouch);
if (newHeight > mPeekHeight) {
if (mPeekAnimator != null) {
mPeekAnimator.cancel();
}
mJustPeeked = false;
} else if (mPeekAnimator == null && mJustPeeked) {
// The initial peek has finished, but we haven't dragged as far yet, lets
// speed it up by starting at the peek height.
mInitialOffsetOnTouch = mExpandedHeight;
mInitialTouchY = y;
mMinExpandHeight = mExpandedHeight;
mJustPeeked = false;
}
newHeight = Math.max(newHeight, mMinExpandHeight);
if (-h >= getFalsingThreshold()) {
mTouchAboveFalsingThreshold = true;
mUpwardsWhenThresholdReached = isDirectionUpwards(x, y);
}
if (!mJustPeeked && (!mGestureWaitForTouchSlop || mTracking)
&& !isTrackingBlocked()) {
// 用户滑动过程会调用setExpandedHeightInternal
setExpandedHeightInternal(newHeight);
}
break;

case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
addMovement(event);
endMotionEvent(event, x, y, false /* forceCancel */);
break;
}
return !mGestureWaitForTouchSlop || mTracking;
}
}

移动过程中:主要在调用了两个方法。
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java

1
2
3
4
5
6
7
protected void onTrackingStarted() {
endClosing();
mTracking = true;
mBar.onTrackingStarted();
notifyExpandingStarted();
notifyBarPanelExpansionChanged();
}

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public void setExpandedHeightInternal(float h) {
if (isNaN(h)) {
Log.wtf(TAG, "ExpandedHeight set to NaN");
}
if (mExpandLatencyTracking && h != 0f) {
DejankUtils.postAfterTraversal(
() -> mLatencyTracker.onActionEnd(LatencyTracker.ACTION_EXPAND_PANEL));
mExpandLatencyTracking = false;
}
float fhWithoutOverExpansion = getMaxPanelHeight() - getOverExpansionAmount();
if (mHeightAnimator == null) {
float overExpansionPixels = Math.max(0, h - fhWithoutOverExpansion);
if (getOverExpansionPixels() != overExpansionPixels && mTracking) {
setOverExpansion(overExpansionPixels, true /* isPixels */);
}
mExpandedHeight = Math.min(h, fhWithoutOverExpansion) + getOverExpansionAmount();
} else {
mExpandedHeight = h;
if (mOverExpandedBeforeFling) {
setOverExpansion(Math.max(0, h - fhWithoutOverExpansion), false /* isPixels */);
}
}

// If we are closing the panel and we are almost there due to a slow decelerating
// interpolator, abort the animation.
if (mExpandedHeight < 1f && mExpandedHeight != 0f && mClosing) {
mExpandedHeight = 0f;
if (mHeightAnimator != null) {
mHeightAnimator.end();
}
}
mExpandedFraction = Math.min(1f,
fhWithoutOverExpansion == 0 ? 0 : mExpandedHeight / fhWithoutOverExpansion);
// 进行锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理
onHeightUpdated(mExpandedHeight);
notifyBarPanelExpansionChanged();
}

下面主要从:onHeightUpdated、notifyBarPanelExpansionChanged 两方法作为入口。
先看 NotificationPanelViewController#onHeightUpdated()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
@Override
protected void onHeightUpdated(float expandedHeight) {
if (!mQsExpanded || mQsExpandImmediate || mIsExpanding && mQsExpandedWhenExpandingStarted) {
// 更新时钟位置将设置顶部填充,这可能会触发新的面板高度并重新定位时钟。
// 这是一个循环依赖项,应该避免,否则会出现堆栈溢出。
if (mStackScrollerMeasuringPass > 2) {
if (DEBUG) Log.d(TAG, "Unstable notification panel height. Aborting.");
} else {
//锁屏上的时间和通知View根据手指的移动距离进行缩小、变透明处理
positionClockAndNotifications();
}
}
if (mQsExpandImmediate || mQsExpanded && !mQsTracking && mQsExpansionAnimator == null
&& !mQsExpansionFromOverscroll) {
float t;
if (mKeyguardShowing) {
// 在Keyguard上,将QS扩展线性插值到面板扩展
t = expandedHeight / (getMaxPanelHeight());
} else {
// In Shade, interpolate linearly such that QS is closed whenever panel height is
// minimum QS expansion + minStackHeight
float
panelHeightQsCollapsed =
mNotificationStackScroller.getIntrinsicPadding()
+ mNotificationStackScroller.getLayoutMinHeight();
float panelHeightQsExpanded = calculatePanelHeightQsExpanded();
t =
(expandedHeight - panelHeightQsCollapsed) / (panelHeightQsExpanded
- panelHeightQsCollapsed);
}
float
targetHeight =
mQsMinExpansionHeight + t * (mQsMaxExpansionHeight - mQsMinExpansionHeight);
setQsExpansion(targetHeight);
}
updateExpandedHeight(expandedHeight);
updateHeader();
// 更新通知半透明
updateNotificationTranslucency();
updatePanelExpanded();
updateGestureExclusionRect();
if (DEBUG) {
mView.invalidate();
}
}

到这里了就一起看个滑动解锁的堆栈:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
12-30 08:32:48.658  1479  1479 D longzhiye  : java.lang.Throwable:
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.keyguard.KeyguardSecurityContainer.showNextSecurityScreenOrFinish(KeyguardSecurityContainer.java:710)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:214)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:196)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.KeyguardBouncer.show(KeyguardBouncer.java:167)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.showBouncer(StatusBarKeyguardViewManager.java:434)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.StatusBar.showBouncerIfKeyguard(StatusBar.java:3959)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.StatusBar.makeExpandedInvisible(StatusBar.java:2506)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.systemui.statusbar.phone.PhoneStatusBarView$1.run(PhoneStatusBarView.java:65)
12-30 08:32:48.658 1479 1479 D longzhiye : at android.os.Handler.handleCallback(Handler.java:938)
12-30 08:32:48.658 1479 1479 D longzhiye : at android.os.Handler.dispatchMessage(Handler.java:99)
12-30 08:32:48.658 1479 1479 D longzhiye : at android.os.Looper.loop(Looper.java:223)
12-30 08:32:48.658 1479 1479 D longzhiye : at android.app.ActivityThread.main(ActivityThread.java:7945)
12-30 08:32:48.658 1479 1479 D longzhiye : at java.lang.reflect.Method.invoke(Native Method)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:603)
12-30 08:32:48.658 1479 1479 D longzhiye : at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

再看一个滑动后到密码安全锁(即密码解锁)的堆栈

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
12-30 09:28:31.8189  1470  1470 D longzhiye: : java.lang.Throwable
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.keyguard.KeyguardSecurityContainer.showNextSecurityScreenOrFinish(KeyguardSecurityContainer.java:710)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:214)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.keyguard.KeyguardHostView.dismiss(KeyguardHostView.java:196)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.KeyguardBouncer.show(KeyguardBouncer.java:167)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.onPanelExpansionChanged(StatusBarKeyguardViewManager.java:297)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.PanelViewController.notifyBarPanelExpansionChanged(PanelViewController.java:1011)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.PanelViewController.setExpandedHeightInternal(PanelViewController.java:727)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.PanelViewController$TouchHandler.onTouch(PanelViewController.java:1338)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.NotificationPanelViewController$18.onTouch(NotificationPanelViewController.java:3229)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.View.dispatchTouchEvent(View.java:14385)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3120)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2792)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.ViewGroup.dispatchTransformedTouchEvent(ViewGroup.java:3126)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.ViewGroup.dispatchTouchEvent(ViewGroup.java:2806)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at com.android.systemui.statusbar.phone.NotificationShadeWindowView.dispatchTouchEvent(NotificationShadeWindowView.java:173)
12-30 09:28:31.8189 1470 1470 D longzhiye: : at android.view.View.dispatchPointerEvent(View.java:14656)
// 省略部分Log....

接着看 PanelViewController#notifyBarPanelExpansionChanged()

这里注意:都是到 KeyguardSecurityContainer#KeyguardSecurityContainer(),滑动解锁与带有密码安全界面,即密码解锁,两个流程的代码执行顺序有差异。

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java

1
2
3
4
5
6
7
8
9
10
11
12
protected void notifyBarPanelExpansionChanged() {
if (mBar != null) {
mBar.panelExpansionChanged(
mExpandedFraction,
mExpandedFraction > 0f || mPeekAnimator != null || mInstantExpanding
|| isPanelVisibleBecauseOfHeadsUp() || mTracking
|| mHeightAnimator != null);
}
for (int i = 0; i < mExpansionListeners.size(); i++) {
mExpansionListeners.get(i).onPanelExpansionChanged(mExpandedFraction, mTracking);
}
}

再 notifyBarPanelExpansionChanged() 方法中继而调用 PanelBar 中的panelExpansionChanged方法:页面的透明度。
PanelBar#panelExpansionChanged()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelBar.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public void panelExpansionChanged(float frac, boolean expanded) {
if (isNaN(frac)) {
throw new IllegalArgumentException("frac cannot be NaN");
}
boolean fullyClosed = true;
boolean fullyOpened = false;
if (SPEW) LOG("panelExpansionChanged: start state=%d", mState);
PanelViewController pv = mPanel;
mExpanded = expanded;
mPanelFraction = frac;
updateVisibility();
// 调整可能部分可见的任何其他面板
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;
}
// fullyOpened 完全打开;就是:锁屏界面是否完全展开的;(手指不滑动时,fullyOpened = true,fullyClosed = false;)
// fullyClosed 完全关闭,就是:锁屏界面是否完全折叠,即消失了;(锁屏界面上滑消失时,fullyOpened = false,fullyClosed = true)
// 手指在滑动过程中时:fullyOpened = false,fullyClosed = false
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":"");
}

当锁屏页面完全消失时,调用 onPanelCollapsed() 方法,执行 post() 方法。
PhoneStatusBarView#onPanelCollapsed()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@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();
}
}
};

接下来执行 StatusBar 中的 makeExpandedInvisible() 方法,更新通知栏和状态栏窗口的可见性。
StatusBar#makeExpandedInvisible()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
void makeExpandedInvisible() {
if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
+ " mExpandedVisible=" + mExpandedVisible);
if (!mExpandedVisible || mNotificationShadeWindowView == null) {
return;
}
// 确保面板完全折叠
mStatusBarView.collapsePanel(/*animate=*/ false, false /* delayed*/,
1.0f /* speedUpFactor */);
mNotificationPanelViewController.closeQs();
mExpandedVisible = false;
visibilityChanged(false);
// 更新通知阴影和状态栏窗口的可见性
mNotificationShadeWindowController.setPanelVisible(false);
mStatusBarWindowController.setForceStatusBarVisible(false);
// Close any guts that might be visible
mGutsManager.closeAndSaveGuts(true /* removeLeavebehind */, true /* force */,
true /* removeControls */, -1 /* x */, -1 /* y */, true /* resetMenu */);
mShadeController.runPostCollapseRunnables();
setInteracting(StatusBarManager.WINDOW_STATUS_BAR, false);
if (!mNotificationActivityStarter.isCollapsingToShowActivityOverLockscreen()) {
showBouncerIfKeyguard();
} else if (DEBUG) {
Log.d(TAG, "Not showing bouncer due to activity showing over lockscreen");
}
mCommandQueue.recomputeDisableFlags(
mDisplayId,
mNotificationPanelViewController.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);
}
}

StatusBar#showBouncerIfKeyguard()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java

1
2
3
4
5
6
7
private void showBouncerIfKeyguard() {
if ((mState == StatusBarState.KEYGUARD || mState == StatusBarState.SHADE_LOCKED)
&& !mKeyguardViewMediator.isHiding()) {
// !mKeyguardViewMediator.isHiding() 不管是滑动解锁还是PIN码解锁等方式都是 true
mStatusBarKeyguardViewManager.showBouncer(true /* scrimmed */);
}
}

StatusBarKeyguardViewManager#showBouncer()
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java

1
2
3
4
5
6
7
public void showBouncer(boolean scrimmed) {
// 如果是滑动解锁,这里 if 条件是 true,如果是密码解锁,这里是 false。
if (mShowing && !mBouncer.isShowing()) {
mBouncer.show(false /* resetSecuritySelection */, scrimmed);
}
updateStates();
}

KeyguardBouncer#show() 如果有设置密码,则显示安全锁界面
frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public void show(boolean resetSecuritySelection, boolean isScrimmed) {
final int keyguardUserId = KeyguardUpdateMonitor.getCurrentUser();
if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) {
// In split system user mode, we never unlock system user.
return;
}
ensureView();
mIsScrimmed = isScrimmed;
if (isScrimmed) {
setExpansion(EXPANSION_VISIBLE);
}

if (resetSecuritySelection) {
showPrimarySecurityScreen();
}

if (mRoot.getVisibility() == View.VISIBLE || mShowingSoon) {
return;
}

final int activeUserId = KeyguardUpdateMonitor.getCurrentUser();
final boolean isSystemUser =
UserManager.isSplitSystemUser() && activeUserId == UserHandle.USER_SYSTEM;
final boolean allowDismissKeyguard = !isSystemUser && activeUserId == keyguardUserId;
// 重点关注 dismiss()
if (allowDismissKeyguard && mKeyguardView.dismiss(activeUserId)) {
return;
}
if (!allowDismissKeyguard) {
Log.w(TAG, "User can't dismiss keyguard: " + activeUserId + " != " + keyguardUserId);
}

mShowingSoon = true;

// Split up the work over multiple frames.
DejankUtils.removeCallbacks(mResetRunnable);
if (mKeyguardStateController.isFaceAuthEnabled() && !needsFullscreenBouncer()
&& !mKeyguardUpdateMonitor.userNeedsStrongAuth()
&& !mKeyguardBypassController.getBypassEnabled()) {
mHandler.postDelayed(mShowRunnable, BOUNCER_FACE_DELAY);
} else {
DejankUtils.postAfterTraversal(mShowRunnable);
}
// 安全锁设置可见性
mCallback.onBouncerVisiblityChanged(true /* shown */);
// 开始显示
mExpansionCallback.onStartingToShow();
}

这里重点关注 mKeyguardView.dismiss(activeUserId) ;
KeyguardHostView#dismiss()
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java

1
2
3
4
5
6
7
8
9
10
11
// 显示安全锁
public boolean dismiss(int targetUserId) {
return dismiss(false, targetUserId, false);
}
@Override
public boolean dismiss(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
// 重点关注
return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId,
bypassSecondaryLockScreen);
}

KeyguardSecurityContainer#showNextSecurityScreenOrFinish()
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// 显示下一个安全屏幕(如果有)
boolean showNextSecurityScreenOrFinish(boolean authenticated, int targetUserId,
boolean bypassSecondaryLockScreen) {
if (DEBUG) Log.d(TAG, "showNextSecurityScreenOrFinish(" + authenticated + ")");
boolean finish = false;
boolean strongAuth = false;
int eventSubtype = -1;
int unLockMode = 0;// add for KFCAANWIKFRA-833
BouncerUiEvent uiEvent = BouncerUiEvent.UNKNOWN;
if (mUpdateMonitor.getUserHasTrust(targetUserId)) {
// 省略部分代码......
} else if (mUpdateMonitor.getUserUnlockedWithBiometric(targetUserId)) {
// 省略部分代码......
} else if (SecurityMode.None == mCurrentSecuritySelection) { // mCurrentSecuritySelection当前安全选择的模式
SecurityMode securityMode = mSecurityModel.getSecurityMode(targetUserId);
if (SecurityMode.None == securityMode) {
unLockMode = 0;
finish = true; // 没有安全锁
eventSubtype = BOUNCER_DISMISS_NONE_SECURITY;
uiEvent = BouncerUiEvent.BOUNCER_DISMISS_NONE_SECURITY;
} else {
showSecurityScreen(securityMode); // switch to the alternate security view
}
} else if (authenticated) {
// mCurrentSecuritySelection 当前锁的模式
switch (mCurrentSecuritySelection) {
case Pattern:
case Password:
case PIN:
unLockMode = mCurrentSecuritySelection.ordinal() - 1;
strongAuth = true;
finish = true;
eventSubtype = BOUNCER_DISMISS_PASSWORD;
uiEvent = BouncerUiEvent.BOUNCER_DISMISS_PASSWORD;
break;
// 省略部分代码......
default:
Log.v(TAG, "Bad security screen " + mCurrentSecuritySelection + ", fail safe");
showPrimarySecurityScreen(false);
break;
}
}
// 检查设备管理员指定的其他安全措施。
/* UNISOC: Modify for bug1394148 @{ */
Intent secondaryLockscreenIntent =
mUpdateMonitor.getSecondaryLockscreenRequirement(targetUserId);
if (finish && !bypassSecondaryLockScreen && secondaryLockscreenIntent != null) {
mSecondaryLockScreenController.show(secondaryLockscreenIntent);
return false;
}
/* @} */
if (eventSubtype != -1) {
mMetricsLogger.write(new LogMaker(MetricsEvent.BOUNCER)
.setType(MetricsEvent.TYPE_DISMISS).setSubtype(eventSubtype));
}
if (uiEvent != BouncerUiEvent.UNKNOWN) {
sUiEventLogger.log(uiEvent);
}
// finish 是否还有一个安全屏幕,即是否解锁完成,有则返回 false,没有返回 true
if (finish) {
// 重点关注
mSecurityCallback.finish(strongAuth, targetUserId);
// 省略部分代码......
}
return finish;
}

KeyguardHostView#finish()
frameworks/base/packages/SystemUI/src/com/android/keyguard/KeyguardHostView.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@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) { // deferKeyguardDone 上面设置成了 false
mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
} else {
// 重点关注
mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
}
}
}

KeyguardViewMediator#keyguardDone()
frameworks/base/packages/SystemUI/src/com/android/systemui/keyguard/KeyguardViewMediator.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    @Override
public void keyguardDone(boolean strongAuth, int targetUserId) {
if (targetUserId != ActivityManager.getCurrentUser()) {
return;
}
if (DEBUG) Log.d(TAG, "keyguardDone");
tryKeyguardDone();
}
private void tryKeyguardDone() {
if (DEBUG) {
Log.d(TAG, "tryKeyguardDone: pending - " + mKeyguardDonePending + ", animRan - "
+ mHideAnimationRun + " animRunning - " + mHideAnimationRunning);
}
if (!mKeyguardDonePending && mHideAnimationRun && !mHideAnimationRunning) {
handleKeyguardDone();
} else if (!mHideAnimationRun) {
if (DEBUG) Log.d(TAG, "tryKeyguardDone: starting pre-hide animation");
mHideAnimationRun = true;
mHideAnimationRunning = true;
mKeyguardViewControllerLazy.get()
.startPreHideAnimation(mHideAnimationFinishedRunnable); // 启动预隐藏动画
}
}
本文链接:
http://longzhiye.top/2023/12/30/2023-12-30/