一、背景
我们经常会遇到一种Application does not hava focused window的ANR异常,这种异常一般是没有焦点窗口FocusedWindow导致,且这类异常只会发生在key事件的派发,因为key事件是需要找到一个焦点窗口然后再派发,而触摸事件只需要找到当前显示的窗口即可
焦点窗口设定
焦点窗口是指当前正在与用户交互的窗口,该窗口负责接收键事件和触摸事件。当启动新的Activity、添加新的窗口、移除旧窗口、分屏来回操作时,都会涉及到焦点窗口的更新。
WMS只管理窗口,无法确定是否有窗口盖住当前画面 SurfaceFlinger管理显示,最贴近于用户看到的画面,可以知道可以知道是否有窗口盖住当前画面,根据真实的显示窗口设置对应的window信息给InputDispatcher
关键日志
1.window
在dumpsys window中查看mCurrentFocus和mFocusedApp,也可以通过如下shell命令来查看当前的FocusWindow:
mCurrentFocus=Window{f96644 u0 NotificationShade}
mFocusedApp=ActivityRecord{e9566ee u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t12}
mCurrentFocus
指的是当前的焦点窗口
mFocusedApp
指的是当前的焦点Activity
$ adb shell dumpsys window d | grep "mCurrentFocus"
mCurrentFocus=Window{135c912 mode=0 rootTaskId=4 u0 com.example.myapplication/com.example.myapplication.MainActivity}
InputWindow是指能接收input事件的窗口,当WMS中状态发生变化后,会将所有符合条件的窗口设置给底层InputFlinger中,在派发事件时,将对从这些窗口中选择目标窗口进行派发,这些窗口就是InputWindow。焦点窗口只有一个,但InputWindow可以有多个。
2.SurfaceFlinger
在dumpsys SurfaceFlinger中查看 HWC layers
Display 4619827259835644672 (active) HWC layers:
---------------------------------------------------------------------------------------------------------------------------------------------------------------
Layer name
Z | Window Type | Comp Type | Transform | Disp Frame (LTRB) | Source Crop (LTRB) | Frame Rate (Explicit) (Seamlessness) [Focused]
---------------------------------------------------------------------------------------------------------------------------------------------------------------
com.example.mysystemdialog/com.example.mysystemdialog.MainActivity#118
rel 0 | 1 | CLIENT | 0 | 0 0 1440 2960 | 0.0 0.0 1440.0 2960.0 | [*]
---------------------------------------------------------------------------------------------------------------------------------------------------------------
StatusBar#75
rel 0 | 2000 | CLIENT | 0 | 0 0 1440 84 | 0.0 0.0 1440.0 84.0 | [ ]
---------------------------------------------------------------------------------------------------------------------------------------------------------------
NavigationBar0#74
rel 0 | 2019 | CLIENT | 0 | 0 2792 1440 2960 | 0.0 0.0 1440.0 168.0 | [ ]
---------------------------------------------------------------------------------------------------------------------------------------------------------------
[Focused]
这一列有带[*]号,则说明是焦点窗口
3.input
在dumpsys input中查看FocusedApplications和FocusedWindows
FocusedApplications:
displayId=0, name='ActivityRecord{e9566ee u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t12}', dispatchingTimeout=5000ms
FocusedWindows:
displayId=0, name='f96644 NotificationShade'
如果发生ANR,焦点窗口以dumpsys input为主
Input Dispatcher State at time of last ANR:
ANR:
Time:......
Reason:......
Window:......
FocusedApplications:......
FocusedWindows: <none>
4.eventlog
11-27 16:15:58.902 3932 4137 I input_focus: [Focus request 5e78d93 com.android.mms/com.android.mms.ui.MmsTabActivity,reason=UpdateInputWindows]
11-27 16:15:58.922 3932 6384 I input_focus: [Focus receive :5e78d93 com.android.mms/com.android.mms.ui.MmsTabActivity,reason=setFocusedWindow]
11-27 16:15:59.027 3932 4436 I input_focus: [Focus entering 5e78d93 com.android.mms/com.android.mms.ui.MmsTabActivity (server),reason=Window became focusable. Previous reason: NOT_VISIBLE]
request
和 entering
正常情况下是一一对应,打印了entering
则表示真正的焦点已经进入到对应的窗口
发生Application does not hava focused window
时,一般request
有打印,我们可以通过是否有entering
的打印来分析
1.entering
部分有打印,代表焦点已经在input里面,但是仍然有ANR,就需要从input等方面分析 2.entering
部分未打印,代表input没有被触发焦点窗口设置到input,需排查SurfaceFlinger或WMS
焦点切换三部log打印位置
java层InputMonitor.java的requestFocus(); // "Focus request"
native层InputDispatcher.cpp的setFocusedWindow(),binder线程; // "Focus receive"
native层InputDispatcher.cpp的dispatchFocusLocked(),InputDispatcher线程 // "Focus " + ("entering " : "leaving ")
二、Focused Window的更新
启动App时更新inputInfo/请求焦点窗口流程:
App主线程调ViewRootImpl.java的relayoutWindow();然后调用到Wms的relayoutWindow(),窗口布局流程。
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java
public int relayoutWindow(Session session, IWindow client, LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, int flags,
ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
SurfaceControl outSurfaceControl, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls, Bundle outSyncIdBundle) {
......
synchronized (mGlobalLock) {
......
if (focusMayChange) {
if (updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/)) {
imMayMove = false;
}
}
......
Binder.restoreCallingIdentity(origId);
//返回result
return result;
}
焦点窗口的更新,通过WMS#updateFocusedWindowLocked()
方法开始,下面从这个方法开始,看下整个更新流程。
2.1 WMS#updateFocusedWindowLocked()
代码路径:framework/services/core/java/com/android/server/wm/WindowManagerService.java
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
// 进入RootWindowContainer中
boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
return changed;
}
其中,第一个参数mode表示更新焦点窗口时所处的阶段,共有五个参数:
// 表示正常更新
static final int UPDATE_FOCUS_NORMAL = 0;
// 表示此次更新焦点窗口发生在window layer分配之前
static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
// 表示此次更新焦点窗口发生在进行放置Surface过程中,在performSurfacePlacement()时
static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
// 表示此次更新焦点窗口发生在进行放置Surface之前
static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
// 表示此次更新焦点窗口发生在焦点窗口移除后
static final int UPDATE_FOCUS_REMOVING_FOCUS = 4;
如在Window添加过程的addWindow()方法中,更新焦点窗口发生在分配窗口layer流程之前,因此使用UPDATE_FOCUS_WILL_ASSIGN_LAYERS作为第一个参数,表示此次更新时,还没有分配layer。 针对不同阶段,会有不同的操作。
第二个参数表示是否同步更新InputWindow,一般在调用的地方会写死
2.2 RootWindowContainer#updateFocusedWindowLocked
代码路径:frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java
/**
* Updates the children's focused window and the top focused display if needed.
*/
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
// 存储了当前焦点窗口的Pid和ActivityRecord的Map映射
mTopFocusedAppByProcess.clear();
boolean changed = false;
int topFocusedDisplayId = INVALID_DISPLAY;
// Go through the children in z-order starting at the top-most
// 遍历DisplayContent
for (int i = mChildren.size() - 1; i >= 0; --i) {
final DisplayContent dc = mChildren.get(i);
// 针对每个DisplayContent,进行焦点窗口更新
changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
// 更新全局焦点窗口
final WindowState newFocus = dc.mCurrentFocus;
if (newFocus != null) {
final int pidOfNewFocus = newFocus.mSession.mPid;
if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);
}
if (topFocusedDisplayId == INVALID_DISPLAY) {
topFocusedDisplayId = dc.getDisplayId();
}
} else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
// The top-most display that has a focused app should still be the top focused
// display even when the app window is not ready yet (process not attached or
// window not added yet).
topFocusedDisplayId = dc.getDisplayId();
}
}
if (topFocusedDisplayId == INVALID_DISPLAY) {
topFocusedDisplayId = DEFAULT_DISPLAY;
}
// 更新mTopFocusedDisplayId,表示焦点屏幕
if (mTopFocusedDisplayId != topFocusedDisplayId) {
mTopFocusedDisplayId = topFocusedDisplayId;
mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
mWmService.mAccessibilityController.setFocusedDisplay(topFocusedDisplayId);
ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "New topFocusedDisplayId=%d", topFocusedDisplayId);
}
return changed;
}
这里会遍历DisplayContent,并在每个DisplayContent中进行更新,然后将更新的结果返回给DisplayContent#mCurrentFocus变量,该变量表示全局的焦点窗口。同时更新mTopFocusedDisplayId变量,表示当前焦点屏(即焦点窗口所在的屏)。
2.3 DisplayContent#updateFocusedWindowLocked
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
int topFocusedDisplayId) {
// 不要自动将焦点重新分配离开应该保持焦点的应用程序窗口。
// findFocusedWindow` 将始终抓取瞬态启动的应用程序,因为它位于“顶部”
// 会造成不匹配,所以早点出去。
if (mCurrentFocus != null && mTransitionController.shouldKeepFocus(mCurrentFocus)
// 这只是保持焦点,所以如果焦点应用程序已被显式更改(例如通过 setFocusedTask),请不要提前退出。
&& mFocusedApp != null && mCurrentFocus.isDescendantOf(mFocusedApp)
&& mCurrentFocus.isVisible() && mCurrentFocus.isFocusable()) {
ProtoLog.v(WM_DEBUG_FOCUS, "Current transition prevents automatic focus change");
return false;
}
// 寻找焦点窗口
WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
if (mCurrentFocus == newFocus) {
return false;
}
boolean imWindowChanged = false;
final WindowState imWindow = mInputMethodWindow;
if (imWindow != null) {
// 更新IME target窗口
final WindowState prevTarget = mImeLayeringTarget;
//获取新的IME Target窗口
final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
imWindowChanged = prevTarget != newTarget;
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
&& mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
//进行window layer的分配
assignWindowLayers(false /* setLayoutNeeded */);
}
// IME target窗口发生变化,重新获取一次焦点窗口
if (imWindowChanged) {
mWmService.mWindowsChanged = true;
setLayoutNeeded();
newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
}
}
ProtoLog.d(WM_DEBUG_FOCUS_LIGHT, "Changing focus from %s to %s displayId=%d Callers=%s",
mCurrentFocus, newFocus, getDisplayId(), Debug.getCallers(4));
//记录旧焦点窗口
final WindowState oldFocus = mCurrentFocus;
// 更新新焦点窗口
mCurrentFocus = newFocus;
if (newFocus != null) {
// 这两个列表用于记录ANR状态,表示自从焦点窗口为空时添加和移除的窗口
mWinAddedSinceNullFocus.clear();
mWinRemovedSinceNullFocus.clear();
if (newFocus.canReceiveKeys()) {
// Displaying a window implicitly causes dispatching to be unpaused.
// This is to protect against bugs if someone pauses dispatching but
// forgets to resume.
// 设置焦点窗口所在mToken.paused属性为false
newFocus.mToken.paused = false;
}
}
getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
mAtmService.mBackNavigationController.onFocusChanged(newFocus);
// IME target窗口发生变化,且旧焦点窗口非输入法窗口时
if (imWindowChanged && oldFocus != mInputMethodWindow) {
// Focus of the input method window changed. Perform layout if needed.
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
performLayout(true /*initial*/, updateInputWindows);
} else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
// Client will do the layout, but we need to assign layers
// for handleNewWindowLocked() below.
assignWindowLayers(false /* setLayoutNeeded */);
}
}
// 向InputMonitor中设置焦点窗口
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
// If we defer assigning layers, then the caller is responsible for doing this part.
getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
}
// 为IME窗口进行调整
adjustForImeIfNeeded();
updateKeepClearAreas();
// We may need to schedule some toast windows to be removed. The toasts for an app that
// does not have input focus are removed within a timeout to prevent apps to redress
// other apps' UI.
scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);
if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
}
// Notify the accessibility manager for the change so it has the windows before the newly
// focused one starts firing events.
// TODO(b/151179149) investigate what info accessibility service needs before input can
// dispatch focus to clients.
if (mWmService.mAccessibilityController.hasCallbacks()) {
mWmService.mH.sendMessage(PooledLambda.obtainMessage(
this::updateAccessibilityOnWindowFocusChanged,
mWmService.mAccessibilityController));
}
return true;
}
上述方法中:
-
通过findFocusedWindowIfNeeded()方法寻找焦点窗口;
-
根据焦点窗口的变化,更新Input Target窗口;
-
更新全局焦点窗口对象mCurrentFocus;
-
根据更新的不同阶段做不同处理。
-
向InputMonitor中设置焦点窗口setInputFocusLw
下面看下如何寻找到焦点窗口。
2.4.DisplayContent#findFocusedWindowIfNeeded()
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
/**
* Looking for the focused window on this display if the top focused display hasn't been
* found yet (topFocusedDisplayId is INVALID_DISPLAY) or per-display focused was allowed.
*
* @param topFocusedDisplayId Id of the top focused display.
* @return The focused window or null if there isn't any or no need to seek.
*/
WindowState findFocusedWindowIfNeeded(int topFocusedDisplayId) {
return (hasOwnFocus() || topFocusedDisplayId == INVALID_DISPLAY)
? findFocusedWindow() : null;
}
当topFocusedDisplayId为INVALID_DISPLAY时,认为当前焦点display没有焦点窗口,需要寻找重新确认,所以又继续执行findFocusedWindow()方法寻找
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
/**
* Find the focused window of this DisplayContent. The search takes the state of the display
* content into account
* @return The focused window, null if none was found.
*/
WindowState findFocusedWindow() {
mTmpWindow = null;
// 遍历windowstate mFindFocusedWindow会在找到新的焦点窗口时填充 mTmpWindow。
forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);
if (mTmpWindow == null) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows, display=%d",
getDisplayId());
return null;
}
return mTmpWindow;
}
该方法中,会遍历所有WindowState,然后将寻找到的WindowState赋值给mTmpWindow,并返回给WMS。接下来看下这个mFindFocusedWindow函数接口对象
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
/**
*用于查找给定窗口的聚焦窗口的 lambda 函数。
如果找到聚焦窗口,则lambda返回true,否则返回 false。
如果找到焦点窗口,它将被存储在mTmpWindow中。
*/
private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
// 当前处于前台的ActivityRecord
final ActivityRecord focusedApp = mFocusedApp;
ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
w, w.mAttrs.flags, w.canReceiveKeys(),
w.canReceiveKeysReason(false /* fromUserTouch */));
// 如果窗口无法接收key事件,则不能作为焦点窗口,返回false
if (!w.canReceiveKeys()) {
return false;
}
// When switching the app task, we keep the IME window visibility for better
// transitioning experiences.
// However, in case IME created a child window or the IME selection dialog without
// dismissing during the task switching to keep the window focus because IME window has
// higher window hierarchy, we don't give it focus if the next IME layering target
// doesn't request IME visible.
if (w.mIsImWindow && w.isChildWindow() && (mImeLayeringTarget == null
|| !mImeLayeringTarget.isRequestedVisible(ime()))) {
return false;
}
if (w.mAttrs.type == TYPE_INPUT_METHOD_DIALOG && mImeLayeringTarget != null
&& !mImeLayeringTarget.isRequestedVisible(ime())
&& !mImeLayeringTarget.isVisibleRequested()) {
return false;
}
final ActivityRecord activity = w.mActivityRecord;
// 如果前台没有Activity,则此次WindowState将作为焦点窗口返回
if (focusedApp == null) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
"findFocusedWindow: focusedApp=null using new focus @ %s", w);
mTmpWindow = w;
return true;
}
// 如果前台Activity是不可获焦的,则此次WindowState将作为焦点窗口返回
if (!focusedApp.windowsAreFocusable()) {
// Current focused app windows aren't focusable...
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: focusedApp windows not"
+ " focusable using new focus @ %s", w);
mTmpWindow = w;
return true;
}
// Descend through all of the app tokens and find the first that either matches
// win.mActivityRecord (return win) or mFocusedApp (return null).
// 如果当前WindowState由ActivityRecord管理,且非StartingWindow,
//则当前台Activity在当前WindowState所属Activity之上时,不存在焦点窗口
if (activity != null && w.mAttrs.type != TYPE_APPLICATION_STARTING) {
if (focusedApp.compareTo(activity) > 0) {
// App root task below focused app root task. No focus for you!!!
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
"findFocusedWindow: Reached focused app=%s", focusedApp);
mTmpWindow = null;
return true;
}
// 如果Activity当前嵌入到焦点任务中,则
// 除非 Activity 与 FocusedApp 位于同一 TaskFragment 上,否则无法获得焦点。
TaskFragment parent = activity.getTaskFragment();
if (parent != null && parent.isEmbedded()) {
if (activity.getTask() == focusedApp.getTask()
&& activity.getTaskFragment() != focusedApp.getTaskFragment()) {
return false;
}
}
}
// 不满足以上条件,则此次WindowState将作为焦点窗口返回
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: Found new focus @ %s", w);
mTmpWindow = w;
return true;
};
-
如果WindowState不能接收Input事件,则不能作为焦点窗口;
-
如果没有前台Activity,则当前WindowState作为焦点窗口返回;
-
如果前台Activity是不可获焦状态,则当前WindowState作为焦点窗口返回;
-
如果当前WindowState由ActivityRecord管理,且该WindowState不是Staring Window类型,那么当前台Activity在当前WindowState所属Activity之上时,不存在焦点窗口;
-
如果Activity当前嵌入到焦点任务中,则除非 Activity与FocusedApp位于同一TaskFragment上,否则无法获得焦点。
-
如果以上条件都不满足,则当前WindowState作为焦点窗口返回;
接下来看一下canReceiveKeys这个函数
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java
public boolean canReceiveKeys(boolean fromUserTouch) {
if (mActivityRecord != null && mTransitionController.shouldKeepFocus(mActivityRecord)) {
// 在瞬态启动期间,瞬态隐藏窗口不可见
// 或在顶部,但保持可聚焦,因此可以接收密钥。
return true;
}
// 可见或处于addWindow()~relayout之间
final boolean canReceiveKeys = isVisibleRequestedOrAdding()
// 客户端View可见 // 退出动画执行完毕
&& (mViewVisibility == View.VISIBLE) && !mRemoveOnExit
// 没有FLAG_NOT_FOCUSABLE标记
&& ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
// mActivityRecord为null或者其可获焦
&& (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
// 可以接受touch事件
&& (mActivityRecord == null || mActivityRecord.getTask() == null
|| !mActivityRecord.getTask().getRootTask().shouldIgnoreInput());
if (!canReceiveKeys) {
return false;
}
// 除非用户故意触摸显示器,否则不允许不受信任的虚拟显示器接收密钥。
return fromUserTouch || getDisplayContent().isOnTop()
|| getDisplayContent().isTrusted();
}
如果一个WindowState可以接受Input事件,需要同时满足多个条件:
-
isVisibleRequestedOrAdding()方法为true,表示该WindowState可见或处于添加过程中:
-
mViewVisibility属性为View.VISIBLE,表示客户端View可见;
-
mRemoveOnExit为false,表示WindowState的退出动画不存在;
-
mAttrs.flags中不存在FLAG_NOT_FOCUSABLE标记,该标记如果设置,表示该窗口为不可获焦窗口;
-
mActivityRecord为null或者mActivityRecord可获焦;
-
shouldIgnoreInput()方法为false,表示可以接受Touch事件。
isVisibleRequestedOrAdding()方法用来判断该WindowState可见或处于添加过程中:
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java
/**
* Is this window capable of being visible (policy and content), in a visible part of the
* hierarchy, and, if an activity window, the activity is visible-requested. Note, this means
* if the activity is going-away, this will be {@code false} even when the window is visible.
*
* The 'adding' part refers to the period of time between IWindowSession.add() and the first
* relayout() -- which, for activities, is the same as visibleRequested.
*
* TODO(b/206005136): This is very similar to isVisibleRequested(). Investigate merging them.
*/
boolean isVisibleRequestedOrAdding() {
final ActivityRecord atoken = mActivityRecord;
// Surface已经创建,说明relayout()已经执行
return (mHasSurface
// relayout()未执行,且客户端View可见状态
|| (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
// 如果是子窗口,其副窗口的mHidden属性为false
&& isVisibleByPolicy() && !isParentWindowHidden()
// mActivityRecord为null,或者其mVisibleRequested为true
&& (atoken == null || atoken.isVisibleRequested())
// 没有执行退出动画 //且Surface没有进行销毁
&& !mAnimatingExit && !mDestroying;
}
如果以上条件都满足,则返回true。
shouldIgnoreInput() 方法用来判断该WindowState是否能够接收touch事件
boolean shouldIgnoreInput() {
//是否支持靠背功能 是否是固定窗口下 且不是Display上的焦点根任务
if (mAtmService.mHasLeanbackFeature && inPinnedWindowingMode()
&& !isFocusedRootTaskOnDisplay()) {
// 防止画中画根任务接收电视上的输入
return true;
}
return false;
}
如果遇到找不到焦点窗口的情况:比如log发现窗口已经是onresume的状态,但是焦点窗口一直未请求切换到此窗口可以查看如下这条log,主要是打印canReceiveKeys为何false的原因(那个属性不对)canReceiveKeysReason此方法
private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
final ActivityRecord focusedApp = mFocusedApp;
ProtoLog.v(WM_DEBUG_FOCUS, "Looking for focus: %s, flags=%d, canReceive=%b, reason=%s",
w, w.mAttrs.flags, w.canReceiveKeys(),
w.canReceiveKeysReason(false /* fromUserTouch */));
if (!w.canReceiveKeys()) {
return false;
......
}
}
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java
/** Returns {@code true} if this window desires key events. */
boolean canReceiveKeys() {
return canReceiveKeys(false /* fromUserTouch */);
}
public String canReceiveKeysReason(boolean fromUserTouch) {
return "fromTouch= " + fromUserTouch
+ " isVisibleRequestedOrAdding=" + isVisibleRequestedOrAdding()
+ " mViewVisibility=" + mViewVisibility
+ " mRemoveOnExit=" + mRemoveOnExit
+ " flags=" + mAttrs.flags
+ " appWindowsAreFocusable="
+ (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
+ " canReceiveTouchInput=" + canReceiveTouchInput()
+ " displayIsOnTop=" + getDisplayContent().isOnTop()
+ " displayIsTrusted=" + getDisplayContent().isTrusted()
+ " transitShouldKeepFocus=" + (mActivityRecord != null
&& mTransitionController.shouldKeepFocus(mActivityRecord));
}
然后我们回到mFindFocusedWindow
接口对象中,其他条件就不一一说明,最终从DisplayContent中自顶向下寻找到的第一个满足条件的窗口,将作为新的焦点窗口后,接下来更新Display#mCurrentFocus
变量,表示当前焦点窗口。
三、InputWindows的更新
找到焦点窗口后,回到DisplayContent的updateFocusedWindowLocked方法中继续往下走 执行到此处会进行InputWindows的更新
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
int topFocusedDisplayId) {
//..........详细看上面....
// 向InputMonitor中设置焦点窗口
if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
// If we defer assigning layers, then the caller is responsible for doing this part.
getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
}
//.........
}
3.1 InputMonitor#updateInputWindowsLw()
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d",
newWindow, mDisplayId);
//这里log打印要切到那个窗口
// 更新当前焦点窗口
final IBinder focus = newWindow != null ? newWindow.mInputChannelToken : null;
if (focus == mInputFocus) {
return;
}
if (newWindow != null && newWindow.canReceiveKeys()) {
// Displaying a window implicitly causes dispatching to be unpaused.
// This is to protect against bugs if someone pauses dispatching but
// forgets to resume.
newWindow.mToken.paused = false;
}
// 更新当前焦点窗口 使mUpdateInputWindowsNeeded设置为true
setUpdateInputWindowsNeededLw();
// 更新所有inputwindow
if (updateInputWindows) {
updateInputWindowsLw(false /*force*/);
}
}
以上过程伴随日志:
WindowManager: Input focus has changed to Window{a44139a u0 NotificationShade} display=0
void setUpdateInputWindowsNeededLw() {
mUpdateInputWindowsNeeded = true;
}
3.2.InputMonitor#updateInputWindowsLw()
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
/* Updates the cached window information provided to the input dispatcher. */
void updateInputWindowsLw(boolean force) {
if (!force && !mUpdateInputWindowsNeeded) {
return;
}
scheduleUpdateInputWindows();
}
在该方法中,只有当强制更新或者mUpdateInputWindowsNeeded
值为true时(在setUpdateInputWindowsNeededLw设置为true),才会进行InputWindows的更新
之后将执行scheduleUpdateInputWindows()
方法,在这个方法中,会post一个Runnable对象mUpdateInputWindows,在mHandler所在的android.anim线程中执行更新流程:
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
private void scheduleUpdateInputWindows() {
//检查窗口是否被移除
if (mDisplayRemoved) {
return;
}
//mUpdateInputWindowsPending只是用来保证post执行不被重复执行,配合锁实现
if (!mUpdateInputWindowsPending) {
mUpdateInputWindowsPending = true;
mHandler.post(mUpdateInputWindows);
}
}
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
private class UpdateInputWindows implements Runnable {
@Override
public void run() {
synchronized (mService.mGlobalLock) {
// 重置变量
//将mUpdateInputWindowsPending 设置为false,准备执行下一次同步
mUpdateInputWindowsPending = false;
mUpdateInputWindowsNeeded = false;
//没有display的话return
if (mDisplayRemoved) {
return;
}
// Populate the input window list with information about all of the windows that
// could potentially receive input.
// As an optimization, we could try to prune the list of windows but this turns
// out to be difficult because only the native code knows for sure which window
// currently has touch focus.
// If there's a drag in flight, provide a pseudo-window to catch drag input
// 是否正在拖拽
final boolean inDrag = mService.mDragDropController.dragDropActiveLocked();
// 在默认显示上添加所有窗口。
mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
}
}
}
private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows();
接下来执行mUpdateInputForAllWindowsConsumer.updateInputWindows()
方法。
3.3.mUpdateInputForAllWindowsConsumer.updateInputWindows()
在该方法中,首先会确认是否存在几类特殊的InputConsumer。InputConsumer用于读取事件,每个窗口对应的客户端都会通过InputConsumer来读取和消费事件,一般情况下,ViewRootImpl在添加窗口过程中,会在注册InputEventReceiver时自动创建InputConsumer对象。此处的特殊InputConsumer则是对一些系统UI显式地进行了创建:
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
// 显式创建的特殊InputConsumer对象
// 用于处理Nav相关input事件
mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
// 用于处理壁纸相关input事件
mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER);
// 用于处理最近的动画输入相关input事件
mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
mAddPipInputConsumerHandle = mPipInputConsumer != null;
mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null;
mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null;
// 重置mInputTransaction
resetInputConsumers(mInputTransaction);
// 如果处于活动状态,则更新最近的输入消费者层
final ActivityRecord activeRecents = getWeak(mActiveRecentsActivity);
if (mAddRecentsAnimationInputConsumerHandle && activeRecents != null
&& activeRecents.getSurfaceControl() != null
WindowContainer layer = getWeak(mActiveRecentsLayerRef);
layer = layer != null ? layer : activeRecents;
// Handle edge-case for SUW where windows don't exist yet
if (layer.getSurfaceControl() != null) {
final WindowState targetAppMainWindow = activeRecents.findMainWindow();
if (targetAppMainWindow != null) {
targetAppMainWindow.getBounds(mTmpRect);
mRecentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
}
mRecentsAnimationInputConsumer.show(mInputTransaction, layer);
mAddRecentsAnimationInputConsumerHandle = false;
}
}
// 遍历窗口更新inputInfo
mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
//调用到Focus Request
updateInputFocusRequest(mRecentsAnimationInputConsumer);
// 将mInputTransaction合并到mPendingTransaction上进行提交
if (!mUpdateInputWindowsImmediately) {
mDisplayContent.getPendingTransaction().merge(mInputTransaction);
mDisplayContent.scheduleAnimation();
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
然后,接mDisplayContent.forAllWindows 将发起所有WindowState的遍历,mUpdateInputForAllWindowsConsumer本身是一个Consumer接口对象,因此会回调accept()
方法对每个WindowState进行处理:
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
boolean forAllWindows(ToBooleanFunction<WindowState> callback, boolean traverseTopToBottom) {
if (traverseTopToBottom) {
for (int i = mChildren.size() - 1; i >= 0; --i) {
if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
return true;
}
}
} else {
final int count = mChildren.size();
for (int i = 0; i < count; i++) {
if (mChildren.get(i).forAllWindows(callback, traverseTopToBottom)) {
return true;
}
}
}
return false;
}
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowState.java
private boolean applyInOrderWithImeWindows(ToBooleanFunction<WindowState> callback,
boolean traverseTopToBottom) {
if (traverseTopToBottom) {
if (applyImeWindowsIfNeeded(callback, traverseTopToBottom)
|| callback.apply(this)) {
return true;
}
} else {
if (callback.apply(this)
|| applyImeWindowsIfNeeded(callback, traverseTopToBottom)) {
return true;
}
}
return false;
}
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
@Override
public boolean apply(WindowState w) {
mConsumer.accept(w);
return false;
}
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
@Override
public void accept(WindowState w) {
// 获取WindowState的InputWindowHandle对象
//WindowState里保存着InputWindowHandle,每次复用,判断changes,减少同步
final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle;
//判断窗口mInputChannelToken是否为空;窗口是否销毁;窗口是否可以接受input事件
if (w.mInputChannelToken == null || w.mRemoved || !w.canReceiveTouchInput()) {
if (w.mWinAnimator.hasSurface()) {
// 确保输入信息无法接收输入事件。可以省略
// 遮挡检测取决于类型或是否是可信覆盖。
populateOverlayInputInfo(inputWindowHandle, w);
setInputWindowInfoIfNeeded(mInputTransaction,
w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
return;
}
// 跳过此窗口,因为它不可能接收输入。
return;
}
// 最近任务是否存在
final RecentsAnimationController recentsAnimationController =
mService.getRecentsAnimationController();
final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
&& recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
if (recentsAnimationController.updateInputConsumerForApp(
mRecentsAnimationInputConsumer.mWindowHandle)) {
final DisplayArea targetDA =
recentsAnimationController.getTargetAppDisplayArea();
if (targetDA != null) {
mRecentsAnimationInputConsumer.reparent(mInputTransaction, targetDA);
mRecentsAnimationInputConsumer.show(mInputTransaction, MAX_VALUE - 2);
mAddRecentsAnimationInputConsumerHandle = false;
}
}
}
// 处理处于PIP模式时的input事件
if (w.inPinnedWindowingMode()) {
if (mAddPipInputConsumerHandle) {
final Task rootTask = w.getTask().getRootTask();
mPipInputConsumer.mWindowHandle.replaceTouchableRegionWithCrop(
rootTask.getSurfaceControl());
final DisplayArea targetDA = rootTask.getDisplayArea();
// We set the layer to z=MAX-1 so that it's always on top.
if (targetDA != null) {
mPipInputConsumer.layout(mInputTransaction, rootTask.getBounds());
mPipInputConsumer.reparent(mInputTransaction, targetDA);
mPipInputConsumer.show(mInputTransaction, MAX_VALUE - 1);
mAddPipInputConsumerHandle = false;
}
}
}
// mWallpaperInputConsumer处理壁纸input事件
if (mAddWallpaperInputConsumerHandle) {
if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisible()) {
mWallpaperInputConsumer.mWindowHandle
.replaceTouchableRegionWithCrop(null /* use this surface's bounds */);
// Add the wallpaper input consumer above the first visible wallpaper.
mWallpaperInputConsumer.show(mInputTransaction, w);
mAddWallpaperInputConsumerHandle = false;
}
}
// 是否处于拖拽过程中
if (mInDrag && w.isVisible() && w.getDisplayContent().isDefaultDisplay) {
mService.mDragDropController.sendDragStartedIfNeededLocked(w);
}
// 注册密钥拦截信息
mService.mKeyInterceptionInfoForToken.put(w.mInputChannelToken,
w.getKeyInterceptionInfo());
if (w.mWinAnimator.hasSurface()) {
// 填充InputWindowHandle
populateInputWindowHandle(inputWindowHandle, w);
//提交inputWindowHandle
setInputWindowInfoIfNeeded(mInputTransaction,
w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
}
}
以上方法中:
-
首先会对几类特殊InputConsumer进行单独处理;
-
然后填充InputWindowHandle对象(populateInputWindowHandle);
-
最后将InputWindowHandle对象设置给Transaction对象(setInputWindowInfoIfNeeded),并在事物提交后,由SurfaceFlinger设置给InputDispatcher中。
3.4.InputMonitor#populateInputWindowHandle()
在该方法中,会将WindowState的部分属性填充给inputWindowHandle:
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
@VisibleForTesting
void populateInputWindowHandle(final InputWindowHandleWrapper inputWindowHandle,
final WindowState w) {
// Add a window to our list of input windows.
inputWindowHandle.setInputApplicationHandle(w.mActivityRecord != null
? w.mActivityRecord.getInputApplicationHandle(false /* update */) : null);
inputWindowHandle.setToken(w.mInputChannelToken);
inputWindowHandle.setDispatchingTimeoutMillis(w.getInputDispatchingTimeoutMillis());
inputWindowHandle.setTouchOcclusionMode(w.getTouchOcclusionMode());
inputWindowHandle.setPaused(w.mActivityRecord != null && w.mActivityRecord.paused);
inputWindowHandle.setWindowToken(w.mClient);
inputWindowHandle.setName(w.getName());
// Update layout params flags to force the window to be not touch modal. We do this to
// restrict the window's touchable region to the task even if it requests touches outside
// its window bounds. An example is a dialog in primary split should get touches outside its
// window within the primary task but should not get any touches going to the secondary
// task.
int flags = w.mAttrs.flags;
if (w.mAttrs.isModal()) {
flags = flags | FLAG_NOT_TOUCH_MODAL;
}
inputWindowHandle.setLayoutParamsFlags(flags);
inputWindowHandle.setInputConfigMasked(
InputConfigAdapter.getInputConfigFromWindowParams(
w.mAttrs.type, flags, w.mAttrs.inputFeatures),
InputConfigAdapter.getMask());
final boolean focusable = w.canReceiveKeys()
&& (mDisplayContent.hasOwnFocus() || mDisplayContent.isOnTop());
inputWindowHandle.setFocusable(focusable);
final boolean hasWallpaper = mDisplayContent.mWallpaperController.isWallpaperTarget(w)
&& !mService.mPolicy.isKeyguardShowing()
&& w.mAttrs.areWallpaperTouchEventsEnabled();
inputWindowHandle.setHasWallpaper(hasWallpaper);
// Surface insets are hardcoded to be the same in all directions
// and we could probably deprecate the "left/right/top/bottom" concept.
// we avoid reintroducing this concept by just choosing one of them here.
inputWindowHandle.setSurfaceInset(w.mAttrs.surfaceInsets.left);
inputWindowHandle.setScaleFactor(w.mGlobalScale != 1f ? (1f / w.mGlobalScale) : 1f);
boolean useSurfaceBoundsAsTouchRegion = false;
SurfaceControl touchableRegionCrop = null;
final Task task = w.getTask();
if (task != null) {
if (task.isOrganized() && task.getWindowingMode() != WINDOWING_MODE_FULLSCREEN) {
// If the window is in a TaskManaged by a TaskOrganizer then most cropping will
// be applied using the SurfaceControl hierarchy from the Organizer. This means
// we need to make sure that these changes in crop are reflected in the input
// windows, and so ensure this flag is set so that the input crop always reflects
// the surface hierarchy. However, we only want to set this when the client did
// not already provide a touchable region, so that we don't ignore the one provided.
if (w.mTouchableInsets != TOUCHABLE_INSETS_REGION) {
useSurfaceBoundsAsTouchRegion = true;
}
if (w.mAttrs.isModal()) {
TaskFragment parent = w.getTaskFragment();
touchableRegionCrop = parent != null ? parent.getSurfaceControl() : null;
}
} else if (task.cropWindowsToRootTaskBounds() && !w.inFreeformWindowingMode()) {
touchableRegionCrop = task.getRootTask().getSurfaceControl();
}
}
inputWindowHandle.setReplaceTouchableRegionWithCrop(useSurfaceBoundsAsTouchRegion);
inputWindowHandle.setTouchableRegionCrop(touchableRegionCrop);
if (!useSurfaceBoundsAsTouchRegion) {
w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs);
inputWindowHandle.setTouchableRegion(mTmpRegion);
}
}
填充完毕InputWindowHandle后,调用setInputWindowInfoIfNeeded()方法
下面简单看一下InputWindowHandleWrapper这个类
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
//InputWindowHandle的包装类
class InputWindowHandleWrapper {
//所有的信息都设置到 mHandle 变量中
private final @NonNull InputWindowHandle mHandle;
boolean isChanged() {
return mChanged;
}
//......
void setTouchableRegion(Region region) {
if (mHandle.touchableRegion.equals(region)) {
return;
}
mHandle.touchableRegion.set(region);
mChanged = true;
}
void applyChangesToSurface(@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl sc) {
t.setInputWindowInfo(sc, mHandle);
mChanged = false;
}
//......
}
代码路径:frameworks/base/core/java/android/view/InputWindowHandle.java
public final class InputWindowHandle {
@Retention(RetentionPolicy.SOURCE)
@IntDef(flag = true, value = {
InputConfig.DEFAULT,
InputConfig.NO_INPUT_CHANNEL,
InputConfig.NOT_FOCUSABLE,
InputConfig.NOT_TOUCHABLE,
InputConfig.PREVENT_SPLITTING,
InputConfig.DUPLICATE_TOUCH_TO_WALLPAPER,
InputConfig.IS_WALLPAPER,
InputConfig.PAUSE_DISPATCHING,
InputConfig.TRUSTED_OVERLAY,
InputConfig.WATCH_OUTSIDE_TOUCH,
InputConfig.SLIPPERY,
InputConfig.DISABLE_USER_ACTIVITY,
InputConfig.SPY,
InputConfig.INTERCEPTS_STYLUS,
})
public InputApplicationHandle inputApplicationHandle;
// The window name.
public String name;
public long dispatchingTimeoutMillis;
public int frameLeft;
public int frameTop;
public int frameRight;
public int frameBottom;
// Window touchable region.
public final Region touchableRegion = new Region();
public int touchOcclusionMode = TouchOcclusionMode.BLOCK_UNTRUSTED;
}
3.5.InputMonitor#setInputWindowInfoIfNeeded()
回到updateInputWindows()
方法的,调用setInputWindowInfoIfNeeded将填充好的InputWindowHandle最终通过通过SurfaceFlinger设置给了InputDispatcher
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
@VisibleForTesting
static void setInputWindowInfoIfNeeded(SurfaceControl.Transaction t, SurfaceControl sc,
InputWindowHandleWrapper inputWindowHandle) {
if (DEBUG_INPUT) {
Slog.d(TAG_WM, "Update InputWindowHandle: " + inputWindowHandle);
}
if (inputWindowHandle.isChanged()) {
inputWindowHandle.applyChangesToSurface(t, sc);
}
}
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputWindowHandleWrapper.java
void applyChangesToSurface(@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl sc) {
t.setInputWindowInfo(sc, mHandle);
mChanged = false;
}
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
/**
* @hide
*/
public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {
//检查条件
checkPreconditions(sc);
nativeSetInputWindowInfo(mNativeObject, sc.mNativeObject, handle);
return this;
}
代码路径:frameworks/base/core/jni/android_view_SurfaceControl.cpp
static void nativeSetInputWindowInfo(JNIEnv* env, jclass clazz, jlong transactionObj,
jlong nativeObject, jobject inputWindow) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
sp<NativeInputWindowHandle> handle = android_view_InputWindowHandle_getHandle(
env, inputWindow);
handle->updateInfo();
auto ctrl = reinterpret_cast<SurfaceControl *>(nativeObject);
transaction->setInputWindowInfo(ctrl, *handle->getInfo());
}
代码路径:frameworks/native/libs/gui/SurfaceComposerClient.cpp
// native层也有对应的surfacecontrol,刚刚封装的WindowInfo也被传递进来
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
const sp<SurfaceControl>& sc, const WindowInfo& info) {
//获取surfaceControl对应的layer_state_t(surfaceflinger的一个图层)
layer_state_t* s = getLayerState(sc);
if (!s) {
mStatus = BAD_INDEX;
return *this;
}
// 把对应的WindowInfo数据设置相应的参数给layer_state_t,就是surface里面的layer
s->windowInfoHandle = new WindowInfoHandle(info);
//在SurfaceFlinger会按照flag来解析改变数据
s->what |= layer_state_t::eInputInfoChanged;
return *this;
}
代码路径:frameworks/native/libs/gui/SurfaceComposerClient.cpp
layer_state_t* SurfaceComposerClient::Transaction::getLayerState(const sp<SurfaceControl>& sc) {
auto handle = sc->getLayerStateHandle();
if (mComposerStates.count(handle) == 0) {
// we don't have it, add an initialized layer_state to our list
ComposerState s;
s.state.surface = handle;
s.state.layerId = sc->getLayerId();
//用于执行apply时,把所有的layer操作一起同步到底层SurfaceFlinger
mComposerStates[handle] = s;
}
return &(mComposerStates[handle].state);
}
后续统一apply(); BpBn调setClientStateLocked()时eInputInfoChanged更新inputInfo;
3.6 InputMonitor#updateInputFocusRequest
再回到InputMonitor#updateInputWindows中
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
// 显式创建的特殊InputConsumer对象
// 用于处理Nav相关input事件
mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
// 用于处理壁纸相关input事件
mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER);
// 用于处理最近的动画输入相关input事件
mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);
mAddPipInputConsumerHandle = mPipInputConsumer != null;
mAddWallpaperInputConsumerHandle = mWallpaperInputConsumer != null;
mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null;
// 重置mInputTransaction
resetInputConsumers(mInputTransaction);
// 如果处于活动状态,则更新最近的输入消费者层
final ActivityRecord activeRecents = getWeak(mActiveRecentsActivity);
if (mAddRecentsAnimationInputConsumerHandle && activeRecents != null
&& activeRecents.getSurfaceControl() != null
WindowContainer layer = getWeak(mActiveRecentsLayerRef);
layer = layer != null ? layer : activeRecents;
// Handle edge-case for SUW where windows don't exist yet
if (layer.getSurfaceControl() != null) {
final WindowState targetAppMainWindow = activeRecents.findMainWindow();
if (targetAppMainWindow != null) {
targetAppMainWindow.getBounds(mTmpRect);
mRecentsAnimationInputConsumer.mWindowHandle.touchableRegion.set(mTmpRect);
}
mRecentsAnimationInputConsumer.show(mInputTransaction, layer);
mAddRecentsAnimationInputConsumerHandle = false;
}
}
// 遍历窗口更新inputInfo
mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
//调用到Focus Request
updateInputFocusRequest(mRecentsAnimationInputConsumer);
// 将mInputTransaction合并到mPendingTransaction上进行提交
if (!mUpdateInputWindowsImmediately) {
mDisplayContent.getPendingTransaction().merge(mInputTransaction);
mDisplayContent.scheduleAnimation();
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
接下先来看一下updateInputFocusRequest是如何打印“Focus request ”这条log的
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
/**
* Called when the current input focus changes.
*/
private void updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer) {
final WindowState focus = mDisplayContent.mCurrentFocus;
//......
final IBinder focusToken = focus != null ? focus.mInputChannelToken : null;
if (focusToken == null) {
if (recentsAnimationInputConsumer != null
&& recentsAnimationInputConsumer.mWindowHandle != null
&& mInputFocus == recentsAnimationInputConsumer.mWindowHandle.token) {
// Avoid removing input focus from recentsAnimationInputConsumer.
// When the recents animation input consumer has the input focus,
// mInputFocus does not match to mDisplayContent.mCurrentFocus. Making it to be
// a special case, that do not remove the input focus from it when
// mDisplayContent.mCurrentFocus is null. This special case should be removed
// once recentAnimationInputConsumer is removed.
return;
}
// 当应用程序获得焦点但其窗口尚未显示时,请从当前窗口中删除输入焦点
// 这强制输入焦点匹配 mDisplayContent.mCurrentFocus
// 但是,如果发现更多特殊情况,即输入焦点和 mDisplayContent.mCurrentFocus 预计不匹配,
//则需要检查如何以及何时撤销焦点的整个逻辑。
if (mDisplayContent.mFocusedApp != null && mInputFocus != null) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "App %s is focused,"
+ " but the window is not ready. Start a transaction to remove focus from"
+ " the window of non-focused apps.",
mDisplayContent.mFocusedApp.getName());
EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Requesting to set focus to null window",
"reason=UpdateInputWindows");
//从具有输入焦点的当前窗口中移除输入焦点。
//仅当当前聚焦的应用程序没有响应并且当前聚焦的窗口不属于当前聚焦的应用程序时才应调用。
mInputTransaction.removeCurrentInputFocus(mDisplayId);
}
mInputFocus = null;
return;
}
//如果当前焦点窗口没有surface或者当前窗口无法聚焦则return
if (!focus.mWinAnimator.hasSurface() || !focus.mInputWindowHandle.isFocusable()) {
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus not requested for window=%s"
+ " because it has no surface or is not focusable.", focus);
mInputFocus = null;
return;
}
requestFocus(focusToken, focus.getName());
}
//将包装出来的InputChannelToken(focusToken)信息向native层进行同步
private void requestFocus(IBinder focusToken, String windowName) {
if (focusToken == mInputFocus) {
return;
}
mInputFocus = focusToken;
//输入焦点请求时间 用于anr的计算
mInputFocusRequestTimeMillis = SystemClock.uptimeMillis();
mInputTransaction.setFocusedWindow(mInputFocus, windowName, mDisplayId);
//在这里打印要请求进入的窗口Focus request
EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + windowName,
"reason=UpdateInputWindows");
ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", windowName);
}
接着看mInputTransaction.setFocusedWindow 这个方法
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
/**
*如果窗口可聚焦,则将焦点设置在由输入 {@code token} 标识的窗口上,否则请求将被丢弃。
*如果窗口不可见,则请求将排队,直到窗口变得可见或该请求被另一个请求覆盖
*当前聚焦的窗口将立即失去焦点。
*这是为了向新获得焦点的窗口发送在完成其第一次绘制时发生的任何焦点调度事件
* @hide
*/
public Transaction setFocusedWindow(@NonNull IBinder token, String windowName,
int displayId) {
nativeSetFocusedWindow(mNativeObject, token, windowName, displayId);
return this;
}
代码路径:frameworks/base/core/jni/android_view_SurfaceControl.cpp
static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
jobject toTokenObj, jstring windowNameJstr, jint displayId) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
if (toTokenObj == NULL) return;
sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));
FocusRequest request;
request.token = toToken;
if (windowNameJstr != NULL) {
ScopedUtfChars windowName(env, windowNameJstr);
request.windowName = windowName.c_str();
}
request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
request.displayId = displayId;
//上面就是一些focusToken的异常检测,没问题,会调用下面的方法
transaction->setFocusedWindow(request);
}
代码路径:frameworks/native/libs/gui/SurfaceComposerClient.cpp
SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
const FocusRequest& request) {
mInputWindowCommands.focusRequests.push_back(request);
return *this;
}
SurfaceComposerClient::Transaction&
SurfaceComposerClient::Transaction::addWindowInfosReportedListener(
sp<gui::IWindowInfosReportedListener> windowInfosReportedListener) {
mInputWindowCommands.windowInfosReportedListeners.insert(windowInfosReportedListener);
return *this;
}
后续统一apply();BpBn调addInputWindowCommands()添加命令;
3.7 merge
再次回到InputMonitor#updateInputWindows()
代码路径:frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java
private void updateInputWindows(boolean inDrag) {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");
//.....
// 遍历窗口更新inputInfo
mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
//调用到Focus Request
updateInputFocusRequest(mRecentsAnimationInputConsumer);
// 将mInputTransaction合并到mPendingTransaction上进行提交
if (!mUpdateInputWindowsImmediately) {
mDisplayContent.getPendingTransaction().merge(mInputTransaction);
mDisplayContent.scheduleAnimation();
}
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
调用SurfaceControl.Transaction#merge
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
/**
* Merge the other transaction into this transaction, clearing the
* other transaction as if it had been applied.
*
* @param other The transaction to merge in to this one.
* @return This transaction.
*/
@NonNull
public Transaction merge(@NonNull Transaction other) {
if (this == other) {
return this;
}
mResizedSurfaces.putAll(other.mResizedSurfaces);
other.mResizedSurfaces.clear();
mReparentedSurfaces.putAll(other.mReparentedSurfaces);
other.mReparentedSurfaces.clear();
nativeMergeTransaction(mNativeObject, other.mNativeObject);
return this;
}
之后,当WindowAnimator.java的animate()时发起apply();可以是线程"android.anim"或"binder"线程;
四、apply
接animate()的openSurfaceTransaction(),prepareSurfaces(),closeSurfaceTransaction()
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowAnimator.java
private void animate(long frameTimeNs) {
if (!mInitialized) {
return;
}
// Schedule next frame already such that back-pressure happens continuously.
scheduleAnimation();
final RootWindowContainer root = mService.mRoot;
mCurrentTime = frameTimeNs / TimeUtils.NANOS_PER_MS;
mBulkUpdateParams = 0;
root.mOrientationChangeComplete = true;
if (DEBUG_WINDOW_TRACE) {
Slog.i(TAG, "!!! animate: entry time=" + mCurrentTime);
}
ProtoLog.i(WM_SHOW_TRANSACTIONS, ">>> OPEN TRANSACTION animate");
mService.openSurfaceTransaction();
try {
// Remove all deferred displays, tasks, and activities.
root.handleCompleteDeferredRemoval();
final AccessibilityController accessibilityController =
mService.mAccessibilityController;
final int numDisplays = root.getChildCount();
for (int i = 0; i < numDisplays; i++) {
final DisplayContent dc = root.getChildAt(i);
// Update animations of all applications, including those associated with
// exiting/removed apps.
dc.updateWindowsForAnimator();
dc.prepareSurfaces();
}
//......
SurfaceControl.mergeToGlobalTransaction(mTransaction);
mService.closeSurfaceTransaction("WindowAnimator");
ProtoLog.i(WM_SHOW_TRANSACTIONS, "<<< CLOSE TRANSACTION animate");
//......
}
-
mService.openSurfaceTransaction(),通过SurfaceControl来通知native开始一个Transaction;
-
mService.closeSurfaceTransaction(),通过SurfaceControl来通知native(SurfaceFlinger)关闭一个Transaction最终来执行合成显示等工作;
4.1 WMS#openSurfaceTransaction
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
void openSurfaceTransaction() {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "openSurfaceTransaction");
SurfaceControl.openTransaction();
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
/** 开启 transaction
* @hide
*/
@UnsupportedAppUsage
public static void openTransaction() {
synchronized (SurfaceControl.class) {
//new出GlobalTransactionWrapper;
if (sGlobalTransaction == null) {
sGlobalTransaction = new GlobalTransactionWrapper();
}
synchronized(SurfaceControl.class) {
sTransactionNestCount++;
}
}
}
New出GlobalTransactionWrapper 接animate()的dc.prepareSurfaces();
4.2 DisplayContent#prepareSurfaces
代码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
@Override
void prepareSurfaces() {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "prepareSurfaces");
try {
final Transaction transaction = getPendingTransaction();
super.prepareSurfaces();
// TODO: Once we totally eliminate global transaction we will pass transaction in here
// rather than merging to global.
SurfaceControl.mergeToGlobalTransaction(transaction);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
将SurfaceControl提交在了mPendingTransaction上。然后完成遍历后,将mPendingTransaction合并到全局Transaction对象上提交给SurfaceFlinger。
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
/**
* Merge the supplied transaction in to the deprecated "global" transaction.
* This clears the supplied transaction in an identical fashion to {@link Transaction#merge}.
* <p>
* This is a utility for interop with legacy-code and will go away with the Global Transaction.
* @hide
*/
@Deprecated
public static void mergeToGlobalTransaction(Transaction t) {
synchronized(SurfaceControl.class) {
sGlobalTransaction.merge(t);
}
}
mergeToGlobalTransaction将提供的Transaction合并提交,然后接animate()调Wms的closeSurfaceTransaction()
4.3 WMS#closeSurfaceTransaction
代码路径:frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
/**
* Closes a surface transaction.
* @param where debug string indicating where the transaction originated
*/
void closeSurfaceTransaction(String where) {
try {
Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "closeSurfaceTransaction");
SurfaceControl.closeTransaction();
mWindowTracing.logState(where);
} finally {
Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
}
}
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
/** end a transaction
* @hide
*/
@UnsupportedAppUsage
//open和close一一对应,保证 sTransactionNestCount 数量
public static void closeTransaction() {
synchronized(SurfaceControl.class) {
if (sTransactionNestCount == 0) {
Log.e(TAG,
"Call to SurfaceControl.closeTransaction without matching openTransaction");
} else if (--sTransactionNestCount > 0) {
return;
}
sGlobalTransaction.applyGlobalTransaction(false);
}
}
再调用SurfaceControl的内部类GlobalTransactionWrapper#applyGlobalTransaction
代码路径:frameworks/base/core/java/android/view/SurfaceControl.java
private static class GlobalTransactionWrapper extends SurfaceControl.Transaction {
void applyGlobalTransaction(boolean sync) {
applyResizedSurfaces();
notifyReparentedSurfaces();
nativeApplyTransaction(mNativeObject, sync);
}
@Override
public void apply(boolean sync) {
throw new RuntimeException("Global transaction must be applied from closeTransaction");
}
}
4.4 SurfaceComposerClient#apply
再调用android_view_SurfaceControl.cpp的nativeApplyTransaction方法
代码路径:frameworks/base/core/jni/android_view_SurfaceControl.cpp
static void nativeApplyTransaction(JNIEnv* env, jclass clazz, jlong transactionObj, jboolean sync) {
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
transaction->apply(sync);
}
代码路径:frameworks/native/libs/gui/SurfaceComposerClient.cpp
status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {
//......
// 遍历mComposerStates,其中包含了所有的InputWindow
for (auto const& kv : mComposerStates){ /
composerStates.add(kv.second);
}
displayStates = std::move(mDisplayStates);
//......
//最后把上面收集的Transaction相关信息,调用sf的setTransactionState进行跨进程传递到sf进程
sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
{} /*uncacheBuffer - only set in doUncacheBufferTransaction*/,
hasListenerCallbacks, listenerCallbacks, mId);
mId = generateId();
// Clear the current states and flags
clear();//apply后就需要把Transaction进行clear
return NO_ERROR;
}
apply方法主要就是收集之前通过transaction属性设置方法设置所有信息都需要收集起来,比如最重要的composerStates,然后调用sf的跨进程方法setTransactionState传递到sf中。
五、SF传递给Input(存疑)
接下来就到了SurfaceFlinger端,内调ISurfaceComposer的setTransactionState()
5.1 SurfaceFlinger::setTransactionState
代码路径:frameworks/native/libs/gui/ISurfaceComposer.cpp
virtual ~BpSurfaceComposer();
status_t setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& state,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
InputWindowCommands commands, int64_t desiredPresentTime, bool isAutoTimestamp,
const std::vector<client_cache_t>& uncacheBuffers, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId,
const std::vector<uint64_t>& mergedTransactionIds) override {
Parcel data, reply;
data.writeInterfaceToken(ISurfaceComposer::getInterfaceDescriptor());
}
status_t BnSurfaceComposer::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
return setTransactionState(frameTimelineInfo, state, displays, stateFlags, applyToken,
std::move(inputWindowCommands), desiredPresentTime,
isAutoTimestamp, uncacheBuffers, hasListenerCallbacks,
listenerCallbacks, transactionId, mergedTransactions);
}
这里是一个BpBn操作,进程surfaceflinger的binder线程,主要是调用到SurfaceFlinger.cpp的setTransactionState()。
代码路径:frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::setTransactionState(
const FrameTimelineInfo& frameTimelineInfo, const Vector<ComposerState>& states,
const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
const InputWindowCommands& inputWindowCommands, int64_t desiredPresentTime,
bool isAutoTimestamp, const client_cache_t& uncacheBuffer, bool hasListenerCallbacks,
const std::vector<ListenerCallbacks>& listenerCallbacks, uint64_t transactionId) {
ATRACE_CALL();
...
TransactionState state{frameTimelineInfo, states,
displays, flags,
applyToken, inputWindowCommands,
desiredPresentTime, isAutoTimestamp,
uncacheBuffer, postTime,
permissions, hasListenerCallbacks,
listenerCallbacks, originPid,
originUid, transactionId};
...
if (mTransactionTracing) {
mTransactionTracing->addQueuedTransaction(state);
}
// 将TransactionState入队,该方法会触发相应的Commit操作
queueTransaction(state);
// Check the pending state to make sure the transaction is synchronous.if (state.transactionCommittedSignal) {
waitForSynchronousTransaction(*state.transactionCommittedSignal);
}
updateSmomoLayerInfo(state, desiredPresentTime, isAutoTimestamp);
return NO_ERROR;
}
5.2 SurfaceFlinger::updateInputFlinger
代码路径:frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
在每次SurfaceFlinger主线程进行commit操作的时候,都会调用一次updateInputFlinger方法去更新一遍需要派发给InputDispatcher的窗口信息,代码如下:
bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
FTL_FAKE_GUARD(kMainThreadContext) {
// and may eventually call to ~Layer() if it holds the last reference
//
updateCursorAsync();
updateInputFlinger(vsyncId, frameTime);
}
转到updateInputFlinger();
代码路径:frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) {
// 如果是没有InputDispatcher或者是没有需要更新的Input窗口信息和窗口命令(如:焦点窗口切换)
// 那么就没有执行updateInputFlinger的必要,直接返回
if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
return;
}
ATRACE_CALL();
std::vector<WindowInfo> windowInfos;
std::vector<DisplayInfo> displayInfos;
bool updateWindowInfo = false;
if (mUpdateInputInfo) {// 需要更新InputInfo的时候会设置为true,默认是false
mUpdateInputInfo = false;
//将updateWindowInfo设置为true
updateWindowInfo = true;
//构建窗口列表信息
buildWindowInfos(windowInfos, displayInfos);
}
std::unordered_set<int32_t> visibleWindowIds;
// 保存最新的可见的WindowInfo
for (WindowInfo& windowInfo : windowInfos) {
if (!windowInfo.inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
visibleWindowIds.insert(windowInfo.id);
}
}
bool visibleWindowsChanged = false;
if (visibleWindowIds != mVisibleWindowIds) {
visibleWindowsChanged = true;
mVisibleWindowIds = std::move(visibleWindowIds);
}
// 设置回调派发WindowInfo给InputDispatcher和调用焦点窗口切换方法
BackgroundExecutor::getInstance().sendCallbacks({[updateWindowInfo,
windowInfos = std::move(windowInfos),
displayInfos = std::move(displayInfos),
inputWindowCommands =
std::move(mInputWindowCommands),
inputFlinger = mInputFlinger, this,
visibleWindowsChanged, vsyncId, frameTime]() {
ATRACE_NAME("BackgroundExecutor::updateInputFlinger");
//通知窗口信息改变
if (updateWindowInfo) {
//通知InputDispatcher更新窗口列表信息
mWindowInfosListenerInvoker
->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
std::move(displayInfos),
vsyncId.value, frameTime.ns()},
std::move(
inputWindowCommands.windowInfosReportedListeners),
/* forceImmediateCall= */ visibleWindowsChanged ||
!inputWindowCommands.focusRequests.empty());
} else {
// 如果有监听器但输入窗口没有变化,则立即调用监听器。
for (const auto& listener : inputWindowCommands.windowInfosReportedListeners) {
if (IInterface::asBinder(listener)->isBinderAlive()) {
listener->onWindowInfosReported();
}
}
}
// focusRequests也是从InputMonitor传过来的,
// InputMonitor的updateInputFocusRequest方法最后会调用requestFocus方法,
//requestFocus方法里面会往InputTransaction里设置focus window。
for (const auto& focusRequest : inputWindowCommands.focusRequests) {
inputFlinger->setFocusedWindow(focusRequest);
}
}});
mInputWindowCommands.clear();
}
接下来看一下构建WindowInfo方法:buildWindowInfos
5.3 SurfaceFlinger::buildWindowInfos
代码路径:frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::buildWindowInfos(std::vector<WindowInfo>& outWindowInfos,
std::vector<DisplayInfo>& outDisplayInfos) {
static size_t sNumWindowInfos = 0;
outWindowInfos.reserve(sNumWindowInfos);
sNumWindowInfos = 0;
if (mLayerLifecycleManagerEnabled) {
mLayerSnapshotBuilder.forEachInputSnapshot(
[&outWindowInfos](const frontend::LayerSnapshot& snapshot) {
outWindowInfos.push_back(snapshot.inputInfo);
});
} else {
//遍历Layer列表
mDrawingState.traverseInReverseZOrder([&](Layer* layer) {
//如果对应Layer不满足needsInputInfo条件,则return
//这里过滤了很大一部分layer
if (!layer->needsInputInfo()) return;
const auto opt =
mFrontEndDisplayInfos.get(layer->getLayerStack())
.transform([](const frontend::DisplayInfo& info) {
return Layer::InputDisplayArgs{&info.transform, info.isSecure};
});
//先通过fillInputinfo填充窗口信息至对应Layer
//再将返回的WindowInfo放入outWindowInfos列表中
outWindowInfos.push_back(layer->fillInputInfo(opt.value_or(Layer::InputDisplayArgs{})));
});
}
sNumWindowInfos = outWindowInfos.size();
outDisplayInfos.reserve(mFrontEndDisplayInfos.size());
for (const auto& [_, info] : mFrontEndDisplayInfos) {
outDisplayInfos.push_back(info.info);
}
需要出现在InputDispatcher的窗口列表中,在SurfaceFlinger这里需要满足两个条件:
-
对应Layer存在于SurfaceFlinger所维护的Layer列表中,
-
对应Layer需要满足needsInputInfo条件。
对于第一个条件,当Layer对应Surface没有被上层destroy时,才会存在于SurfaceFlinger的Layer列表中。(上层移除Surface的时机一般为对应窗口退出动画执行完毕时,由relayoutWindow流程发起)
对于第二个条件,需要对应窗口的inputChannelToken建立且没有被移除。
5.4 Layer::fillInputInfo 设置可见性
代码路径:frameworks/native/services/surfaceflinger/Layer.cpp
WindowInfo Layer::fillInputInfo(const InputDisplayArgs& displayArgs) {
WindowInfo info = mDrawingState.inputInfo;
// 设置WindowInfo可见性
info.visible = isVisibleForInput();
info.alpha = getAlpha();
fillTouchOcclusionMode(info);
handleDropInputMode(info);
// 不可见的WindowInfo就在这里设置上WindowInfo::InputConfig::NOT_VISIBLE
// 后续派发到InputDispatcher中就能根据该flag判断是否可见
info.setInputConfig(WindowInfo::InputConfig::NOT_VISIBLE, !info.visible);
return info;
}
看一下可见性在Layer::isVisibleForInput中是如何判断的:
代码路径:frameworks/native/services/surfaceflinger/Layer.h
virtual bool isVisibleForInput() const {
// For compatibility reasons we let layers which can receive input
// receive input before they have actually submitted a buffer. Because
// of this we use canReceiveInput instead of isVisible to check the
// policy-visibility, ignoring the buffer state. However for layers with
// hasInputInfo()==false we can use the real visibility state.
// We are just using these layers for occlusion detection in
// InputDispatcher, and obviously if they aren't visible they can't occlude
// anything.
// 对于具不具备InputInfo的layer,有两种可见性的判断逻辑,对于输入窗口,走canReceiveInput
return hasInputInfo() ? canReceiveInput() : isVisible();
}
代码路径:frameworks/native/services/surfaceflinger/Layer.cpp
对于具有InputInfo的Layer,走的是canReceiveInput的逻辑判断当前是否可见:
bool Layer::canReceiveInput() const {
return !isHiddenByPolicy() && (mBufferInfo.mBuffer == nullptr || getAlpha() > 0.0f);
}
这里通过三个条件作可见性判断:
-
isHiddenByPolicy:这里会检查目标Layer的flag是否带有不可见标识
-
mBufferInfo.mBuffer:buffer为空即满足可见条件
-
getAlpha:Alpha值大于0,非透明即可见
详细的有关layer可见性的本篇不再继续跟踪
5.5 WindowInfosListenerInvoker::windowInfosChanged
回到SurfaceFlinger::updateInputFlinger方法,我们继续跟一下windowInfosChanged
void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) {
// 如果是没有InputDispatcher或者是没有需要更新的Input窗口信息和窗口命令(如:焦点窗口切换)
// 那么 就没有执行updateInputFlinger的必要,直接返回
if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
return;
}
ATRACE_CALL();
//......
if (updateWindowInfo) {
//通知InputDispatcher更新窗口列表信息
mWindowInfosListenerInvoker
->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
std::move(displayInfos),
vsyncId.value, frameTime.ns()},
std::move(
inputWindowCommands.windowInfosReportedListeners),
/* forceImmediateCall= */ visibleWindowsChanged ||
!inputWindowCommands.focusRequests.empty());
}
//.....
mInputWindowCommands.clear();
}
windowInfosChanged方法能够将WindowInfo的变化通知到InputDispatcher。
代码路径:frameworks/native/services/surfaceflinger/WindowInfosListenerInvoker.cpp
void WindowInfosListenerInvoker::windowInfosChanged(
gui::WindowInfosUpdate update, WindowInfosReportedListenerSet reportedListeners,
bool forceImmediateCall) {
// ......
for (auto& pair : mWindowInfosListeners) {
auto& [listenerId, listener] = pair.second;
// listener其实就是InputDispather
auto status = listener->onWindowInfosChanged(update);
if (!status.isOk()) {
ackWindowInfosReceived(update.vsyncId, listenerId);
}
}
// ......
}
内调InputDispatcher.cpp内部类DispatcherWindowListener的onWindowInfosChanged();
代码路径:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(
const gui::WindowInfosUpdate& update) {
mDispatcher.onWindowInfosChanged(update);
}
void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) {
// The listener sends the windows as a flattened array. Separate the windows by display for
// more convenient parsing.
std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
for (const auto& info : update.windowInfos) {
handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
handlesPerDisplay[info.displayId].push_back(sp<WindowInfoHandle>::make(info));
}
{ // acquire lock
std::scoped_lock _l(mLock);
//确保我们为所有现有显示器创建了一个条目,
//以便如果 displayId 没有窗口,我们可以知道窗口已从显示器中删除。
for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
handlesPerDisplay[displayId];
}
mDisplayInfos.clear();
for (const auto& displayInfo : update.displayInfos) {
mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
}
//遍历,handlesPerDisplay,调setInputWindowsLocked()
for (const auto& [displayId, handles] : handlesPerDisplay) {
setInputWindowsLocked(handles, displayId);
}
if (update.vsyncId < mWindowInfosVsyncId) {
ALOGE("Received out of order window infos update. Last update vsync id: %" PRId64
", current update vsync id: %" PRId64,
mWindowInfosVsyncId, update.vsyncId);
}
mWindowInfosVsyncId = update.vsyncId;
}
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
//遍历,handlesPerDisplay,调setInputWindowsLocked() 由sf侧传递给input侧
六、Input之FocusWindow切换
6.1 InputDispatcher::setInputWindowsLocked
代码路径:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
// 对每一个windowInfo进行判断和处理
void InputDispatcher::setInputWindowsLocked(
const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
...
// Copy old handles for release if they are no longer present.
const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);
// Save the old windows' orientation by ID before it gets updated.
std::unordered_map<int32_t, uint32_t> oldWindowOrientations;
for (const sp<WindowInfoHandle>& handle : oldWindowHandles) {
oldWindowOrientations.emplace(handle->getId(),
handle->getInfo()->transform.getOrientation());
}
//更新mWindowHandlesByDisplay这个map,然后通过getWindowHandlesLocked()
//找newFocusedWindowHandle
updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);
const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
if (mLastHoverWindowHandle &&
std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
windowHandles.end()) {
mLastHoverWindowHandle = nullptr;
}
// 对FocusChanges进行处理,同步给FocusResolver来更新窗口状态
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setInputWindows(displayId, windowHandles);
if (changes) {
onFocusChangedLocked(*changes);
}
...
}
6.2 FocusResolver::setInputWindows
代码路径:frameworks/native/services/inputflinger/dispatcher/FocusResolver.cpp
/**
* 当窗口属性更改时,将调用“setInputWindows”。这里我们将检查当前聚焦的窗口是否可以保持聚焦
* 如果当前聚焦的窗口仍然有资格获得焦点(“isTokenFocusable”返回 OK),那么我们将继续授予它焦点
* 否则我们将检查先前的焦点请求是否有资格获得焦点。
*/
std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
std::string removeFocusReason;
//请求焦点,WMS把focusRequest发给surfaceFlinger,surfaceFlinger传递到这里。
const std::optional<FocusRequest> request = getFocusRequest(displayId);
// 获取当前的焦点窗口
const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
// 根据最新的 FocusRequest 查找下一个焦点令牌。如果请求的焦点窗口无法获得焦点,则焦点将被移除。
if (request) {
sp<IBinder> requestedFocus = request->token;
sp<WindowInfoHandle> resolvedFocusWindow;
Focusability result = getResolvedFocusWindow(requestedFocus, windows, resolvedFocusWindow);
if (result == Focusability::OK && resolvedFocusWindow->getToken() == currentFocus) {
return std::nullopt;
}
const Focusability previousResult = mLastFocusResultByDisplay[displayId];
mLastFocusResultByDisplay[displayId] = result;
//只有获取的状态为ok
if (result == Focusability::OK) {
LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
"Focused window should be non-null when result is OK!");
// 如果可以获取焦点,则更新焦点窗口
return updateFocusedWindow(displayId,
"Window became focusable. Previous reason: " +
ftl::enum_string(previousResult),
resolvedFocusWindow->getToken(),
resolvedFocusWindow->getName());
}
removeFocusReason = ftl::enum_string(result);
}
// 无法获取焦点,则焦点为空
return updateFocusedWindow(displayId, removeFocusReason, nullptr);
}
6.3 FocusResolver::isTokenFocusable判断焦点窗口状态
代码路径:frameworks/native/services/inputflinger/dispatcher/FocusResolver.cpp
FocusResolver::Focusability FocusResolver::isTokenFocusable(
const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
sp<WindowInfoHandle>& outFocusableWindow) {
bool allWindowsAreFocusable = true;
bool windowFound = false;// 默认设置窗口未找到
sp<WindowInfoHandle> visibleWindowHandle = nullptr;
// 遍历InputDispatcher中保存的所有窗口信息
for (const sp<WindowInfoHandle>& window : windows) {
if (window->getToken() != token) {// 一直走这个分支的话,就是找不到目标窗口
continue;
}
windowFound = true;
// 目标窗口的inputConfig不包含NOT_VISIBLE,那么窗口就是可见的
if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
// Check if at least a single window is visible.
visibleWindowHandle = window;
}
// 目标窗口的inputConfig包含NOT_FOCUSABLE,那么窗口就是不能获取焦点
if (window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_FOCUSABLE)) {
// Check if all windows with the window token are focusable.
allWindowsAreFocusable = false;
break;
}
}
// 根据前面的遍历查找结果设置焦点窗口状态
if (!windowFound) {
return Focusability::NO_WINDOW;
}
if (!allWindowsAreFocusable) {
return Focusability::NOT_FOCUSABLE;
}
if (!visibleWindowHandle) {
return Focusability::NOT_VISIBLE;
}
// 仅当窗口可以聚焦时才设置 outFoundWindow
outFocusableWindow = visibleWindowHandle;
return Focusability::OK;
}
这里要遍历的windows是从SurfaceFlinger发来的最新的输入窗口列表,而token是之前设置的焦点窗口,这个函数的大意是根据最新的输入窗口信息判断之前设置的焦点窗口是否有效。
那么可能有几种情况,即对应Focusability的4个值:
-
返回NO_WINDOW,说明之前设置的焦点窗口已经不在最新的输入窗口列表里了,即该输入窗口的Layer已经被移除了,或者不满足Layer.needsInputInfo的条件
-
返回NOT_FOCUSABLE,说明之前设置的焦点窗口还在最新的输入窗口列表里,但是被设置了NOT_FOCUSABLE这个标志位,不满足作为焦点窗口的条件了
-
返回NOT_VISIBLE,说明之前设置的焦点窗口还在最新的输入窗口列表里,但是被设置了NOT_VISIBLE,即该Layer已经不可见了,所以不能再作为焦点窗口了
-
返回OK,找到了一个符合条件的窗口作为焦点窗口,并且将该窗口保存在传参outFocusableWindow中
private:
enum class Focusability {
OK,
NO_WINDOW,
NOT_FOCUSABLE,
NOT_VISIBLE,
ftl_last = NOT_VISIBLE
};
6.4 InputDispatcher::setFocusedWindow
异步BpBn;//进程system_server的binder线程调用InputManager.cpp的setFocusedWindow()
代码路径:frameworks/native/services/inputflinger/InputManager.cpp
binder::Status InputManager::setFocusedWindow(const FocusRequest& request) {
mDispatcher->setFocusedWindow(request);
return binder::Status::ok();
}
内调InputDispatcher.cpp的setFocusedWindow();
代码路径:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
/**
* 将焦点设置到由标记标识的窗口。必须在更新任何输入窗口句柄后调用此函数。
*
* Params:
* request.token - 输入通道令牌用于标识应该获得焦点的窗口
* request.focusedToken - 调用者期望当前关注的令牌。如果指定的令牌与当前聚焦的窗口不匹配,则该请求将被丢弃。
* 如果指定的焦点标记与当前焦点窗口匹配,则调用将成功。
* 如果无论当前聚焦的令牌是什么,此调用都应该成功,则将其设置为“null”
* request.timestamp - 请求焦点更改时客户端 (wm) 设置的 SYSTEM_TIME_MONOTONIC 时间戳(以纳秒为单位)。
* 如果存在来自另一个源(例如指针向下)的焦点更改请求,这将确定哪个请求优先。
*/
void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
{ // acquire lock
std::scoped_lock _l(mLock);
std::optional<FocusResolver::FocusChanges> changes =
mFocusResolver.setFocusedWindow(request, getWindowHandlesLocked(request.displayId));
if (changes) {
onFocusChangedLocked(*changes);
}
} // release lock
// Wake up poll loop since it may need to make new input dispatching choices.
mLooper->wake();
}
先调用FocusResolver.cpp的setFocusedWindow()
6.5 FocusResolver::updateFocusedWindow
代码路径:frameworks/native/services/inputflinger/dispatcher/FocusResolver.cpp
/**
* 当窗口属性更改时,将调用“setInputWindows”。这里我们将检查当前聚焦的窗口是否可以保持聚焦
* 如果当前聚焦的窗口仍然有资格获得焦点(“isTokenFocusable”返回 OK),那么我们将继续授予它焦点
* 否则我们将检查先前的焦点请求是否有资格获得焦点。
*/
std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
std::string removeFocusReason;
//请求焦点,WMS把focusRequest发给surfaceFlinger,surfaceFlinger传递到这里。
const std::optional<FocusRequest> request = getFocusRequest(displayId);
// 获取当前的焦点窗口
const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
// 根据最新的 FocusRequest 查找下一个焦点令牌。如果请求的焦点窗口无法获得焦点,则焦点将被移除。
if (request) {
sp<IBinder> requestedFocus = request->token;
sp<WindowInfoHandle> resolvedFocusWindow;
Focusability result = getResolvedFocusWindow(requestedFocus, windows, resolvedFocusWindow);
if (result == Focusability::OK && resolvedFocusWindow->getToken() == currentFocus) {
return std::nullopt;
}
const Focusability previousResult = mLastFocusResultByDisplay[displayId];
mLastFocusResultByDisplay[displayId] = result;
//只有获取的状态为ok
if (result == Focusability::OK) {
LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
"Focused window should be non-null when result is OK!");
// 如果可以获取焦点,则更新焦点窗口
return updateFocusedWindow(displayId,
"Window became focusable. Previous reason: " +
ftl::enum_string(previousResult),
resolvedFocusWindow->getToken(),
resolvedFocusWindow->getName());
}
removeFocusReason = ftl::enum_string(result);
}
// 无法获取焦点,则焦点为空
return updateFocusedWindow(displayId, removeFocusReason, nullptr);
}
调用updateFocusedWindow 新的焦点窗口newFocus赋值给mFocusedWindowTokenByDisplay
代码路径:frameworks/native/services/inputflinger/dispatcher/FocusResolver.cpp
std::optional<FocusResolver::FocusChanges> FocusResolver::updateFocusedWindow(
int32_t displayId, const std::string& reason, const sp<IBinder>& newFocus,
const std::string& tokenName) {
sp<IBinder> oldFocus = getFocusedWindowToken(displayId);
if (newFocus == oldFocus) {
return std::nullopt;
}
if (newFocus) {
//赋值mFocusRequestByDisplay
mFocusedWindowTokenByDisplay[displayId] = {tokenName, newFocus};
} else {
mFocusedWindowTokenByDisplay.erase(displayId);
}
return {{oldFocus, newFocus, displayId, reason}};
}
//赋值mFocusRequestByDisplay
6.6 InputDispatcher::dispatchFocusLocked
我们回到InputDispatcher::setFocusedWindow方法,继续调用InputDispatcher::onFocusChangedLocked方法
代码路径:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::onFocusChangedLocked(const FocusResolver::FocusChanges& changes) {
if (changes.oldFocus) {
std::shared_ptr<InputChannel> focusedInputChannel = getInputChannelLocked(changes.oldFocus);
if (focusedInputChannel) {
CancelationOptions options(CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS,
"focus left window");
synthesizeCancelationEventsForInputChannelLocked(focusedInputChannel, options);
enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason);
}
}
if (changes.newFocus) {
enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason);
}
disablePointerCaptureForcedLocked();
if (mFocusedDisplayId == changes.displayId) {
sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
}
}
调用enqueueFocusEventLocked方法,接下来是一系列调用堆栈
代码路径:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
const std::string& reason) {
if (mPendingEvent != nullptr) {
// Move the pending event to the front of the queue. This will give the chance
// for the pending event to get dispatched to the newly focused window
mInboundQueue.push_front(mPendingEvent);
mPendingEvent = nullptr;
}
std::unique_ptr<FocusEntry> focusEntry =
std::make_unique<FocusEntry>(mIdGenerator.nextId(), now(), windowToken, hasFocus,
reason);
//该事件应该位于队列的前面,但位于所有其他焦点事件之后
// 找到最后一个焦点事件,并在其后面插入
std::deque<std::shared_ptr<EventEntry>>::reverse_iterator it =
std::find_if(mInboundQueue.rbegin(), mInboundQueue.rend(),
[](const std::shared_ptr<EventEntry>& event) {
return event->type == EventEntry::Type::FOCUS;
});
// 维护焦点事件的顺序。在所有其他焦点事件之后插入条目。
mInboundQueue.insert(it.base(), std::move(focusEntry));
}
代码路径:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnce() {
nsecs_t nextWakeupTime = LLONG_MAX;
{ // acquire lock
std::scoped_lock _l(mLock);
mDispatcherIsAlive.notify_all();
// Run a dispatch loop if there are no pending commands.
// The dispatch loop might enqueue commands to run afterwards.
if (!haveCommandsLocked()) {
dispatchOnceInnerLocked(&nextWakeupTime);
}
//.......
}
代码路径:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
nsecs_t currentTime = now();
//......
case EventEntry::Type::FOCUS: {
std::shared_ptr<FocusEntry> typedEntry =
std::static_pointer_cast<FocusEntry>(mPendingEvent);
dispatchFocusLocked(currentTime, typedEntry);
done = true;
dropReason = DropReason::NOT_DROPPED; // focus events are never dropped
break;
}
//......
}
dispatchFocusLocked关键log"Focus entering" 或 "Focus leaving" 打印地方
代码路径:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime, std::shared_ptr<FocusEntry> entry) {
std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
if (channel == nullptr) {
return; // Window has gone away
}
InputTarget target;
target.inputChannel = channel;
target.flags = InputTarget::Flags::DISPATCH_AS_IS;
entry->dispatchInProgress = true;
std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
channel->getName();
std::string reason = std::string("reason=").append(entry->reason);
android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS;
dispatchEventLocked(currentTime, entry, {target});
}
在这里打印日志"Focus entering" 或 "Focus leaving"
代码路径:frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp
void InputDispatcher::dispatchEventLocked(nsecs_t currentTime,
std::shared_ptr<EventEntry> eventEntry,
const std::vector<InputTarget>& inputTargets) {
ATRACE_CALL();
if (DEBUG_DISPATCH_CYCLE) {
ALOGD("dispatchEventToCurrentInputTargets");
}
updateInteractionTokensLocked(*eventEntry, inputTargets);
ALOG_ASSERT(eventEntry->dispatchInProgress); // should already have been set to true
pokeUserActivityLocked(*eventEntry);
for (const InputTarget& inputTarget : inputTargets) {
std::shared_ptr<Connection> connection =
getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
if (connection != nullptr) {
prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
} else {
if (DEBUG_FOCUS) {
ALOGD("Dropping event delivery to target with channel '%s' because it "
"is no longer registered with the input dispatcher.",
inputTarget.inputChannel->getName().c_str());
}
}
}
总结:
焦点窗口切换涉及模块较多,需要经过WMS->SF->Input三个模块传递文章来源:https://www.toymoban.com/news/detail-851203.html
其中对sf传给input这段相关的通信存疑,网上资料也比较少且安卓U上也有函数改动,希望能有大佬补充,上面相关流程有些地方可能不对,涉及到多个进程。希望相关大佬能够补充分析,感谢文章来源地址https://www.toymoban.com/news/detail-851203.html
到了这里,关于安卓焦点窗口切换的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!