安卓焦点窗口切换

一、背景

我们经常会遇到一种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]

requestentering正常情况下是一一对应,打印了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;
}

上述方法中:

  1. 通过findFocusedWindowIfNeeded()方法寻找焦点窗口;

  2. 根据焦点窗口的变化,更新Input Target窗口;

  3. 更新全局焦点窗口对象mCurrentFocus;

  4. 根据更新的不同阶段做不同处理。

  5. 向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;
};
  1. 如果WindowState不能接收Input事件,则不能作为焦点窗口;

  2. 如果没有前台Activity,则当前WindowState作为焦点窗口返回;

  3. 如果前台Activity是不可获焦状态,则当前WindowState作为焦点窗口返回;

  4. 如果当前WindowState由ActivityRecord管理,且该WindowState不是Staring Window类型,那么当前台Activity在当前WindowState所属Activity之上时,不存在焦点窗口;

  5. 如果Activity当前嵌入到焦点任务中,则除非 Activity与FocusedApp位于同一TaskFragment上,否则无法获得焦点。

  6. 如果以上条件都不满足,则当前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事件,需要同时满足多个条件:

  1. isVisibleRequestedOrAdding()方法为true,表示该WindowState可见或处于添加过程中:

  2. mViewVisibility属性为View.VISIBLE,表示客户端View可见;

  3. mRemoveOnExit为false,表示WindowState的退出动画不存在;

  4. mAttrs.flags中不存在FLAG_NOT_FOCUSABLE标记,该标记如果设置,表示该窗口为不可获焦窗口;

  5. mActivityRecord为null或者mActivityRecord可获焦;

  6. 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);
    }
}

以上方法中:

  1. 首先会对几类特殊InputConsumer进行单独处理;

  2. 然后填充InputWindowHandle对象(populateInputWindowHandle);

  3. 最后将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");
    //......
}
  1. mService.openSurfaceTransaction(),通过SurfaceControl来通知native开始一个Transaction;

  2. 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这里需要满足两个条件:

  1. 对应Layer存在于SurfaceFlinger所维护的Layer列表中,

  2. 对应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);
}

这里通过三个条件作可见性判断:

  1. isHiddenByPolicy:这里会检查目标Layer的flag是否带有不可见标识

  2. mBufferInfo.mBuffer:buffer为空即满足可见条件

  3. 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个值:

  1. 返回NO_WINDOW,说明之前设置的焦点窗口已经不在最新的输入窗口列表里了,即该输入窗口的Layer已经被移除了,或者不满足Layer.needsInputInfo的条件

  2. 返回NOT_FOCUSABLE,说明之前设置的焦点窗口还在最新的输入窗口列表里,但是被设置了NOT_FOCUSABLE这个标志位,不满足作为焦点窗口的条件了

  3. 返回NOT_VISIBLE,说明之前设置的焦点窗口还在最新的输入窗口列表里,但是被设置了NOT_VISIBLE,即该Layer已经不可见了,所以不能再作为焦点窗口了

  4. 返回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三个模块传递

其中对sf传给input这段相关的通信存疑,网上资料也比较少且安卓U上也有函数改动,希望能有大佬补充,上面相关流程有些地方可能不对,涉及到多个进程。希望相关大佬能够补充分析,感谢

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/539937.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python零基础从小白打怪升级中~~~~~~~FaskAPI中的请求和响应

第二节&#xff1a;FastAPI中请求数据 一、URL请求参数 url请求参数是通过url请求地址携带的&#xff0c;例如&#xff0c;在以下 url 中&#xff1a; http://127.0.0.1:8000/items/?skip0&limit10这些请求参数是键值对的集合&#xff0c;这些键值对位于 URL 的 &#…

php反序列化(2)

一.pop链 在反序列化中&#xff0c;我们能控制的数据就是对象中的属性值&#xff08;成员变量&#xff09;&#xff0c;所以在php反序列化中有一种漏洞利用方法叫“面向属性编程”&#xff0c;即pop&#xff08;property oriented programming&#xff09;。 pop链就是利用魔…

ES6基础(JavaScript基础)

本文用于检验学习效果&#xff0c;忘记知识就去文末的链接复习 1. ECMAScript介绍 ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协会&#xff0c;英文名称是European Computer Manufacturers Association&#xff09;通过ECMA-262标准化的脚本程序设计语言…

python(使用循环显示四种模式)

代码&#xff1a; # 模式A n int(input("请输入三角形的层数")) for i in range(1,n 1):for j in range(1,i 1):print(f"{j}\t", end" ")print()# 模式B n int(input("请输入三角形的层数")) for i in range(1,n 1):for j in rang…

点击notify里面的通知,实现路由跳转

需求描述&#xff1a; 右上角有出来通知用户的有代办任务的消息框&#xff0c;点击消息框会跳转到代办路由页面。 duration:3000//弹窗显示时间, 毫秒 getElementsByClassName() – 获取所有指定类名的元素 效果展示&#xff1a;

32单片机入门持续更新中

配套资料为野火霸道V2 初识 STM32 4.1 什么是 STM32 STM32&#xff0c;从字面上来理解&#xff0c;ST 是意法半导体&#xff0c;M 是 Microelectronics 的缩写&#xff0c;32 表示 32 位&#xff0c;合起 来理解&#xff0c;STM32 就是指 ST 公司开发的 32 位微控制器。在如今…

7.基础乐理-重升重降号、等音扩展篇

在 6.升降号、黑键的音名 这里知道了一个等音的概念&#xff0c;就是指的是同一个键&#xff0c;同一个音&#xff0c;拥有不同的名字&#xff0c;这些名字互相称为等音 在音乐中除了升降号&#xff0c;还有两个东西&#xff0c;一个长得像 x 叫重&#xff08;chong&#xff09…

React Router 5 vs 6:使用上的主要差异与升级指南

React Router 5 的一些API 在 React Router 6 上有时可能找不到&#xff0c;可能会看到如下画面&#xff1a;export ‘useHistory’ was not found in ‘react-router-dom’ … React Router目前有两个大的版本&#xff0c;即React Router 5、6。React Router 6 在设计上更加简…

分布式系统接口限流方案

Git地址&#xff1a;https://gitee.com/deepjava/test-api-limit.git 方案一、 Guava工具包 实现单机版限流 具体代码见git 方案二、Redis lua脚本 实现分布式系统的接口限流 具体代码见git

纯css实现左右拖拽改变盒子大小

效果&#xff1a; 代码 <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html;charsetutf-8"><title></title><style>body {background-color: black;color: white;}.column {ove…

Linux C柔性数组(零长数组)

零长数组&#xff0c;大小为0&#xff0c;一般用在结构体中&#xff08;网络通信&#xff0c;省流&#xff09;&#xff0c;节省空间&#xff0c;方便善后&#xff08;相对于指针类型&#xff09;&#xff0c;我们通过具体例子进行理解。 常规定长数组 #include <stdio.h&…

MongoDB的安装和使用

1.MongoDB 安装 1.1 基于Docker安装 docker run --restartalways -d --name mongo -v /opt/mongodb/data:/data/db -p 27017:27017 mongo:4.0.6 1.2 客户端工具使用 MongoDB Compass | MongoDB 2.MongoDB 使用 2.1 引用依赖包 <dependency><groupId>org.sprin…

软件无线电系列——抽取器的多相滤波和内插器的多相滤波

本节目录 一、抽取器的多相滤波结构 二、内插器的多相滤波结构 三、一个抽取器多相滤波器的设计本节内容 从前面文章中可以知道&#xff0c;抽取器模型中的低通滤波器在抽取算子D之前&#xff0c;是在降低速率之前实现的&#xff1b;内插器模型中的低通滤波器在内插算子I之后&…

DedeCMS 未授权远程命令执行漏洞分析

dedecms介绍 DedeCMS是国内专业的PHP网站内容管理系统-织梦内容管理系统&#xff0c;采用XML名字空间风格核心模板&#xff1a;模板全部使用文件形式保存&#xff0c;对用户设计模板、网站升级转移均提供很大的便利&#xff0c;健壮的模板标签为站长DIY自己的网站提供了强有力…

[数据结构]—二叉树基本概念

1.树概念及结构 1.树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根朝上&#xff0c;而叶朝下的。 有一个特殊的结点&#xff…

计算机网络 子网掩码与划分子网

一、实验要求与内容 1、需拓扑图和两个主机的IP配置截图。 2、设置网络A内的主机IP地址为“192.168.班内学号.2”&#xff0c;子网掩码为“255.255.255.128”&#xff0c;网关为“192.168.班内学号.1”&#xff1b;设置网络B内的主机IP地址为“192.168.班内学号100.2”&#…

【LeetCode】二叉树类题目详解

二叉树 二叉树的理论基础 二叉树是结点的度数之和不超过2的树&#xff0c;二叉树总共有五种基本形态 二叉树的种类主要有&#xff1a; 满二叉树完全二叉树 二叉树的存储方式 顺序存储链式存储 二叉树的遍历方式 先序遍历&#xff08;深度优先搜索&#xff09;中序遍历&…

css里面的浮动笔记

参考链接&#xff1a; (图文详细)最通俗易懂的CSS 浮动float属性详解_css float简单理解-CSDN博客 经验分享&#xff1a;CSS浮动(float,clear)通俗讲解 - 杨元 - 博客园 (cnblogs.com) 要点&#xff1a; 浮动元素只会影响后面标准流的元素 &#xff0c;如果在它之前有一个标…

AndroidAutomotive模块介绍(三)CarService服务

前言 上一篇文档总结 Android Automotive 框架的 APP 和 API 部分内容&#xff0c;本篇文档将会继续根据 Android Automotive 框架结构&#xff0c;总结 Framework 层 CarService 服务的内容。 本文档对 Android Automotive Framework 层服务将会按照如下顺序展开描述&#x…

学习ArkTS -- 常用组件使用

学习ArkTS 使用Deveco studio写ArkTSImage: 图片显示组件1.声明Image组件并设置图片源2. 添加图片属性 Text: 文本显示组件1. 声明Text组件并设置文本内容2. 添加文本属性 TextInput&#xff1a;文本输入框1. 声明TextInput2. 添加属性和事件 Button 组件1. 声明Button组件&…