WMS& SF& IMS: 焦点窗口更新框架

在工作中,不乏一些做稳定性的"小朋友",当遇到无法解决的"无焦点窗口"的ANR时,就会与我做一番"友好"的交流,有时候真让我不胜其烦。说实话,这些东西都是做稳定性的基本功。

本文先来分析焦点窗口的更新框架,后面将分析 app 冷启动的焦点窗口更新流程,最后再来探讨下焦点窗口与"无焦点窗口" ANR 的关系。

更新焦点窗口

一般来说,当出现新窗口时,如果窗口可以接收按键事件,那么它就可以成为焦点窗口。此时需调用 WindowManagerService#updateFocusedWindowLocked() 来更新焦点窗口,如下

java 复制代码
// WindowManagerService.java

boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
    Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "wmUpdateFocus");
    boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
    Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    return changed;
}
java 复制代码
// RootWindowContainer.java

boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
    // ...
    
    // RootWindowContainer 遍历所有 DisplayContent,更新它们的焦点窗口
    for (int i = mChildren.size() - 1; i >= 0; --i) {
        final DisplayContent dc = mChildren.get(i);
        
        // 更新每一个 DisplayContent 的焦点窗口
        changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
        
        // ...
    }
    
    // ...
    
    return changed;
}
java 复制代码
// DisplayContent.java

/**
 * Update the focused window and make some adjustments if the focus has changed.
 *
 * @param mode Indicates the situation we are in. Possible modes are:
 *             {@link WindowManagerService#UPDATE_FOCUS_NORMAL},
 *             {@link WindowManagerService#UPDATE_FOCUS_PLACING_SURFACES},
 *             {@link WindowManagerService#UPDATE_FOCUS_WILL_PLACE_SURFACES},
 *             {@link WindowManagerService#UPDATE_FOCUS_REMOVING_FOCUS}
 * @param updateInputWindows Whether to sync the window information to the input module.
 * @param topFocusedDisplayId Display id of current top focused display.
 * @return {@code true} if the focused window has changed.
 */
boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows,
        int topFocusedDisplayId) {
        
    // shell-transitions 开启的情况下,近期任务动画的焦点窗口,不是在这里自动更新的
    // Don't re-assign focus automatically away from a should-keep-focus app window.
    // `findFocusedWindow` will always grab the transient-launch app since it is "on top" which
    // would create a mismatch, so just early-out here.
    if (mCurrentFocus != null && mTransitionController.shouldKeepFocus(mCurrentFocus)
            // This is only keeping focus, so don't early-out if the focused-app has been
            // explicitly changed (eg. via setFocusedTask).
            && mFocusedApp != null && mCurrentFocus.isDescendantOf(mFocusedApp)
            && mCurrentFocus.isVisible() && mCurrentFocus.isFocusable()) {
        ProtoLog.v(WM_DEBUG_FOCUS, "Current transition prevents automatic focus change");
        return false;
    }
    
    // 1. 获取 DisplayContent 下的焦点窗口
    WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
    
    if (mCurrentFocus == newFocus) {
        return false;
    }
    
    // ...

    // WMS 焦点窗口改变的 log
    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 保存的就是 DisplayContent 的焦点窗口
    mCurrentFocus = newFocus;

    // ...

    // DisplayPolicy 主要使用 mFocusedWindow 保存新焦点窗口,
    // 使用 mLastFocusedWindow 保存前一个焦点窗口
    getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
    
    // ...

    // 2. mode 不是 UPDATE_FOCUS_WILL_ASSIGN_LAYERS 时,才向输入系统更新焦点窗口
    // add window 时的焦点窗口更新,使用的就是 UPDATE_FOCUS_WILL_ASSIGN_LAYERS,
    // 然后更新 layer,最后补一个焦点窗口更新。所以,这里不会更新焦点窗口
    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);
    }

    // ...

    return true;
}

DisplayContent 会先获取焦点窗口,然后通过 InputMonitor ,把焦点窗口设置给底层。

获取焦点窗口

java 复制代码
// DisplayContent.java

WindowState findFocusedWindowIfNeeded(int topFocusedDisplayId) {
    return (hasOwnFocus() || topFocusedDisplayId == INVALID_DISPLAY)
                ? findFocusedWindow() : null;
}


WindowState findFocusedWindow() {
    mTmpWindow = null;

    // 从上到下遍历所有窗口,获取焦点窗口,并保存到 mTmpWindow
    // mFindFocusedWindow will populate mTmpWindow with the new focused window when found.
    forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);

    if (mTmpWindow == null) {
        // 没有找到焦点窗口的 log
        ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: No focusable windows, display=%d",
                getDisplayId());
        return null;
    }
    return mTmpWindow;
}


private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
    // 当前焦点 app
    final ActivityRecord focusedApp = mFocusedApp;
    
    // 寻找焦点窗口的 log
    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;
    }

    // ...

    // 窗口所属的 Activity
    final ActivityRecord activity = w.mActivityRecord;

    // 当前 DisplayContent 没有焦点 app,那么就让当前遍历的窗口成为焦点窗口
    if (focusedApp == null) {
        ProtoLog.v(WM_DEBUG_FOCUS_LIGHT,
                "findFocusedWindow: focusedApp=null using new focus @ %s", w);
        mTmpWindow = w;
        return true;
    }

    // 焦点 app 下的所有窗口都不能获得焦点,那么让当前遍历的窗口,成为焦点窗口
    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).
    if (activity != null && w.mAttrs.type != TYPE_APPLICATION_STARTING) {
        // 如果窗口的 activity 的 layer 低于当前焦点 app 的 layer,那么这个窗口不能成为焦点窗口
        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;
        }

        // If the candidate activity is currently being embedded in the focused task, the
        // activity cannot be focused unless it is on the same TaskFragment as the focusedApp's.
        TaskFragment parent = activity.getTaskFragment();
        if (parent != null && parent.isEmbedded()) {
            if (activity.getTask() == focusedApp.getTask()
                    && activity.getTaskFragment() != focusedApp.getTaskFragment()) {
                return false;
            }
        }
    }

    // 找到焦点窗口的 log
    ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "findFocusedWindow: Found new focus @ %s", w);
    
    // 保存焦点窗口
    mTmpWindow = w;
    
    return true;
};

从上到下遍历 DisplayContent 的所有窗口,一般来说,只要窗口可以接收按键事件,那么就可以成为焦点窗口。

窗口可以接收事件的条件有点多,我大致标注下,如下

java 复制代码
// WindowState.java

boolean canReceiveKeys() {
    return canReceiveKeys(false /* fromUserTouch */);
}


public boolean canReceiveKeys(boolean fromUserTouch) {
    // 近期任务动画,比较特殊
    if (mActivityRecord != null && mTransitionController.shouldKeepFocus(mActivityRecord)) {
        // During transient launch, the transient-hide windows are not visibleRequested
        // or on-top but are kept focusable and thus can receive keys.
        return true;
    }
    
    // 主要是下面这些条件,用于判断窗口是否可以接收按键事件
    final boolean canReceiveKeys = isVisibleRequestedOrAdding()
            // app 端 View 可见
            && (mViewVisibility == View.VISIBLE) 
            // mRemoveOnExit 我前面文章解释过,表示在 exit animation 后移除窗口
            && !mRemoveOnExit
            // 不能设置 FLAG_NOT_FOCUSABLE 
            && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0)
            && (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch))
            // can it receive touches
            && (mActivityRecord == null || mActivityRecord.getTask() == null
                    || !mActivityRecord.getTask().getRootTask().shouldIgnoreInput());

    if (!canReceiveKeys) {
        return false;
    }
    // Do not allow untrusted virtual display to receive keys unless user intentionally
    // touches the display.
    return fromUserTouch || getDisplayContent().isOnTop()
            || getDisplayContent().isTrusted();
}


boolean isVisibleRequestedOrAdding() {
    final ActivityRecord atoken = mActivityRecord;
    // 有 surface 或者 可见并且正在添加窗口
    return (mHasSurface || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
            // 策略可见
            && isVisibleByPolicy() 
            // 父窗口不能隐藏
            && !isParentWindowHidden()
            // ActivityRecord 请求可见
            && (atoken == null || atoken.isVisibleRequested())
            // 没有执行 exit animation 动画
            && !mAnimatingExit 
            // 没有销毁窗口
            && !mDestroying;
}

设置焦点窗口

java 复制代码
// InputMonitor.java

/**
 * Called when the current input focus changes. Will apply it in next updateInputWindows.
 * Layer assignment is assumed to be complete by the time this is called.
 */
void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {

    ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Input focus has changed to %s display=%d",
            newWindow, mDisplayId);
            
    final IBinder focus = newWindow != null ? newWindow.mInputChannelToken : null;
    if (focus == mInputFocus) {
        return;
    }

    if (newWindow != null && newWindow.canReceiveKeys()) {
        newWindow.mToken.paused = false;
    }

    // 仅仅是把标志位 mUpdateInputWindowsNeeded 设置为 true
    setUpdateInputWindowsNeededLw();

    // 如果函数参数 updateInputWindows 为 true,才能调度去更新输入窗口信息到输入系统
    // 在这期间,会更新焦点窗口给底层
    if (updateInputWindows) {
        updateInputWindowsLw(false /*force*/);
    }
}

DisplayContent 焦点窗口改变后,会通过 InputMonitor 下发给底层。具体的方法是,先设置标志为 mUpdateInputWindowsNeeded 为 true,表示需要更新输入窗口给底层,然后调度更新输入窗口,来完成发送焦点窗口给底层。

更新输入窗口

一般来说,只要窗口改变,例如可见性改变,就需要向底层更新输入窗口,这其中也伴随着下发焦点窗口,如下

java 复制代码
// InputMonitor.java

void updateInputWindowsLw(boolean force) {
    // 要么强制更新,要么设置 mUpdateInputWindowsNeeded 标志位后更新
    if (!force && !mUpdateInputWindowsNeeded) {
        return;
    }
    
    scheduleUpdateInputWindows();
}


private void scheduleUpdateInputWindows() {
    if (mDisplayRemoved) {
        return;
    }

    if (!mUpdateInputWindowsPending) {
        mUpdateInputWindowsPending = true;
        // mHandler 使用的是动画线程
        mHandler.post(mUpdateInputWindows);
    }
}


private final UpdateInputWindows mUpdateInputWindows = new UpdateInputWindows();


private class UpdateInputWindows implements Runnable {
    @Override
    public void run() {
        synchronized (mService.mGlobalLock) {
            mUpdateInputWindowsPending = false;
            mUpdateInputWindowsNeeded = false;

            if (mDisplayRemoved) {
                return;
            }

            final boolean inDrag = mService.mDragDropController.dragDropActiveLocked();

            // Add all windows on the default display.
            mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
        }
    }
}


private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {

    private void updateInputWindows(boolean inDrag) {
        Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateInputWindows");

        // ...
        
        // 1. 如果窗口有改变,收集它的信息
        mDisplayContent.forAllWindows(this, true /* traverseTopToBottom */);
        
        // 2. 更新焦点窗口请求
        updateInputFocusRequest(mRecentsAnimationInputConsumer);

        // 3. DisplayContent pending transaction 合并 mInputTransaction ,然后在动画线程中 apply
        if (!mUpdateInputWindowsImmediately) {
            mDisplayContent.getPendingTransaction().merge(mInputTransaction);
            mDisplayContent.scheduleAnimation();
        }

        Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
    }
}

更新输入窗口有三步

  1. 收集有改变的窗口的信息。
  2. 更新焦点窗口请求。
  3. 前两步的操作都是保存到 mInputTransaction,现在把它 merge 到 DisplayContent pending transaction,最后在动画线程中 apply,即下发到 SurfaceFlinger。

收集有改变的窗口的信息

java 复制代码
// InputMonitor.java

private final class UpdateInputForAllWindowsConsumer implements Consumer<WindowState> {

    public void accept(WindowState w) {
        final InputWindowHandleWrapper inputWindowHandle = w.mInputWindowHandle;
        
        // ...

        // 前提条件是窗口必须有 surface
        if (w.mWinAnimator.hasSurface()) {
            // 1. 把输入窗口信息填充到 inputWindowHandle
            populateInputWindowHandle(inputWindowHandle, w);
            // 2. 如果窗口有改变,通过 mInputTransaction 保存窗口的信息
            setInputWindowInfoIfNeeded(mInputTransaction,
                    w.mWinAnimator.mSurfaceController.mSurfaceControl, inputWindowHandle);
        }
    }
}

DisplayContent 从上到下遍历所有窗口,先把窗口信息收集到 InputWindowHandleWrapper,如下

java 复制代码
// InputMonitor.java

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);

    // If we are scaling the window, input coordinates need to be inversely scaled to map from
    // what is on screen to what is actually being touched in the UI.
    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) {
            // ...
        } else if (task.cropWindowsToRootTaskBounds() && !w.inFreeformWindowingMode()) {
            // ...
        }
    }
    inputWindowHandle.setReplaceTouchableRegionWithCrop(useSurfaceBoundsAsTouchRegion);
    inputWindowHandle.setTouchableRegionCrop(touchableRegionCrop);

    if (!useSurfaceBoundsAsTouchRegion) {
        // 从 WindowState 中获取 touch region,一般就是 window frame 大小
        w.getSurfaceTouchableRegion(mTmpRegion, w.mAttrs);
        // 保存的是窗口的可触摸区域
        inputWindowHandle.setTouchableRegion(mTmpRegion);
    }
}

信息填充完毕后,会检测哪些窗口改变,然后通过 mInputTransaction 保存窗口的信息,如下

java 复制代码
// InputMonitor.java

static void setInputWindowInfoIfNeeded(SurfaceControl.Transaction t, SurfaceControl sc,
        InputWindowHandleWrapper inputWindowHandle) {
    if (DEBUG_INPUT) {
        Slog.d(TAG_WM, "Update InputWindowHandle: " + inputWindowHandle);
    }
    
    // 只有改变了,才需要把窗口信息更新到 SurfaceFlinger
    if (inputWindowHandle.isChanged()) {
        inputWindowHandle.applyChangesToSurface(t, sc);
    }
}
java 复制代码
// InputWindowHandleWrapper.java

void applyChangesToSurface(@NonNull SurfaceControl.Transaction t, @NonNull SurfaceControl sc) {
    // 通过 mInputTransaction 保存有改变的窗口的信息
    t.setInputWindowInfo(sc, mHandle);
    mChanged = false;
}
java 复制代码
// SurfaceControl.java

public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {
    checkPreconditions(sc);
    nativeSetInputWindowInfo(mNativeObject, sc.mNativeObject, handle);
    return this;
}
cpp 复制代码
// 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());
}
java 复制代码
// frameworks/native/libs/gui/SurfaceComposerClient.cpp

SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setInputWindowInfo(
        const sp<SurfaceControl>& sc, const WindowInfo& info) {
    
    // 从 std::unordered_map<sp<IBinder>, ComposerState, IBinderHash> mComposerStates 获取
    layer_state_t* s = getLayerState(sc);
    if (!s) {
        mStatus = BAD_INDEX;
        return *this;
    }
    s->windowInfoHandle = new WindowInfoHandle(info);
    s->what |= layer_state_t::eInputInfoChanged;
    return *this;
}

SurfaceComposerClient::Transaction 的 mComposerStates 保存了所有需要更新的 layer 的信息,之后 apply mInputTransaction 时,数据会发送到 SurfaceFlinger。

更新焦点窗口请求

DisplayContent 保存了所有有改变窗口的信息后,然后下发焦点窗口请求

java 复制代码
// InputMonitor.java

private void updateInputFocusRequest(InputConsumerImpl recentsAnimationInputConsumer) {

    // 获取 DisplayContent 的焦点窗口   
    final WindowState focus = mDisplayContent.mCurrentFocus;
    
    if (recentsAnimationInputConsumer != null && focus != null) {
        // ...
    }

    final IBinder focusToken = focus != null ? focus.mInputChannelToken : null;
    if (focusToken == null) {
        // ...
    }

    // 焦点窗口没有 surface 或者不能获得焦点,不需要设置焦点窗口到输入系统
    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());
}


private void requestFocus(IBinder focusToken, String windowName) {
    if (focusToken == mInputFocus) {
        return;
    }

    // 保存向输入系统更新的焦点窗口 input channel token
    mInputFocus = focusToken;
    mInputFocusRequestTimeMillis = SystemClock.uptimeMillis();
    
    // 通过 transaction 更新焦点窗口
    mInputTransaction.setFocusedWindow(mInputFocus, windowName, mDisplayId);
    
    // WMS 向底层更新焦点窗口请求的 event log,例如
    // input_focus: [Focus request f33c665 com.awesome.helloworld/com.awesome.helloworld.MainActivity,reason=UpdateInputWindows]
    EventLog.writeEvent(LOGTAG_INPUT_FOCUS, "Focus request " + windowName,
            "reason=UpdateInputWindows");
            
    ProtoLog.v(WM_DEBUG_FOCUS_LIGHT, "Focus requested for window=%s", windowName);
}
cpp 复制代码
// android_view_SurfaceControl.cpp

static void nativeSetFocusedWindow(JNIEnv* env, jclass clazz, jlong transactionObj,
                                   jobject toTokenObj, jstring windowNameJstr, jint displayId) {
    
    // 转化为底层的 SurfaceComposerClient::Transaction
    auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
    if (toTokenObj == NULL) return;
    
    // token 转化为 IBinder
    sp<IBinder> toToken(ibinderForJavaObject(env, toTokenObj));

    // 创建 FocusRequest,保存传入的几个参数
    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;
    
    // Transaction 保存 FocusRequest
    transaction->setFocusedWindow(request);
}
cpp 复制代码
// frameworks/native/libs/gui/SurfaceComposerClient.cpp

SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setFocusedWindow(
        const FocusRequest& request) {
    mInputWindowCommands.focusRequests.push_back(request);
    return *this;
}

SurfaceComposerClient::Transaction 使用 mInputWindowCommands.focusRequests 保存焦点窗口请求。

apply mInputTransaction

WMS 层的 Transaction 对应的是底层的 SurfaceComposerClient::Transaction,它的 apply 如下

cpp 复制代码
// SurfaceComposerClient.cpp

// synchronous 为 false
// oneWay 默认为 false
status_t SurfaceComposerClient::Transaction::apply(bool synchronous, bool oneWay) {
    // ...

    // mComposerStates 保存的是 layer 信息,现在复制到 composerStates
    Vector<ComposerState> composerStates;
    for (auto const& kv : mComposerStates) {
        composerStates.add(kv.second);
    }


    // ...

    sp<ISurfaceComposer> sf(ComposerService::getComposerService());
    
    // composerStates 保存了需要更新的窗口的信息
    // mInputWindowCommands 保存了焦点窗口请求
    sf->setTransactionState(mFrameTimelineInfo, composerStates, displayStates, flags, applyToken,
                            mInputWindowCommands, mDesiredPresentTime, mIsAutoTimestamp,
                            mUncacheBuffers, hasListenerCallbacks, listenerCallbacks, mId,
                            mMergedTransactionIds);
    // ...

    // 清理 mComposerStates、mDisplayStates、mInputWindowCommands、mListenerCallbacks 等等
    clear();

    // ...
    
    return NO_ERROR;
}

SurfaceComposerClient::Transaction 把很多数据发送给 SurfaceFlinger,其中 composerStates 保存了刚才更新的窗口的信息,mInputWindowCommands 保存了焦点请求。现在看下 SurfaceFlinger 如何处理

cpp 复制代码
// SurfaceFlinger.cpp

status_t SurfaceFlinger::setTransactionState(
        const FrameTimelineInfo& frameTimelineInfo, Vector<ComposerState>& states,
        const Vector<DisplayState>& displays, uint32_t flags, const sp<IBinder>& applyToken,
        InputWindowCommands inputWindowCommands, 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) {
    
    // ...

    // 解析所有 layer,保存到 resolvedStates
    std::vector<ResolvedComposerState> resolvedStates;
    resolvedStates.reserve(states.size());
    for (auto& state : states) {
        resolvedStates.emplace_back(std::move(state));
        // ...
    }

    // TransactionState 包装所有参数
    TransactionState state{frameTimelineInfo,
                           resolvedStates,
                           displays,
                           flags,
                           applyToken,
                           std::move(inputWindowCommands),
                           desiredPresentTime,
                           isAutoTimestamp,
                           std::move(uncacheBufferIds),
                           postTime,
                           hasListenerCallbacks,
                           listenerCallbacks,
                           originPid,
                           originUid,
                           transactionId,
                           mergedTransactionIds};

    // ...

    // 保存到 mTransactionHandler.mLocklessTransactionQueue 队列中
    mTransactionHandler.queueTransaction(std::move(state));
    
    // 请求 Vsync 来处理 transaction
    setTransactionFlags(eTransactionFlushNeeded, schedule, applyToken, frameHint);
    return NO_ERROR;
}


void SurfaceFlinger::setTransactionFlags(uint32_t mask, TransactionSchedule schedule,
                                         const sp<IBinder>& applyToken, FrameHint frameHint) {
    mScheduler->modulateVsync({}, &VsyncModulator::setTransactionSchedule, schedule, applyToken);
    
    // 按位或更新 mTransactionFlags,并返回旧值
    // mTransactionFlags 保存了 eTransactionFlushNeeded 标志位
    uint32_t transactionFlags = mTransactionFlags.fetch_or(mask);
    ATRACE_INT("mTransactionFlags", transactionFlags);
    
    // 旧值 transactionFlags 不包含 mask 位,代表没有调度 VSync
    if (const bool scheduled = transactionFlags & mask; !scheduled) {
        scheduleCommit(frameHint);
    } else if (frameHint == FrameHint::kActive) {
       // ...
    }
}

这里是以 Android U 的 SurfaceFlinger 代码进行分析的。而前面是以 Android V 代码分析的。

SurfaceFlinger 收到 apply transaction 的数据后,把数据包装到 TransactionState,然后保存到 TransactionHandler::mLocklessTransactionQueue 中,然后请求 Vsync 来处理 transcation。

当 VSync 信号到来,会先调用 SurfaceFlinger::commit() 准备数据,然后调用 SurfaceFlinger::composite() 合成 layer。 这里,我们主要看下 commit() 如何处理 transcation,如下

cpp 复制代码
// SurfaceFlinger.cpp

bool SurfaceFlinger::commit(TimePoint frameTime, VsyncId vsyncId, TimePoint expectedVsyncTime)
        FTL_FAKE_GUARD(kMainThreadContext) {
    // ...

    // Composite if transactions were committed, or if requested by HWC.
    bool mustComposite = mMustComposite.exchange(false);
    {
        mFrameTimeline->setSfWakeUp(vsyncId.value, frameTime.ns(),
                                    Fps::fromPeriodNsecs(vsyncPeriod.ns()));

        // 清除并检测是否有 eTransactionFlushNeeded 标志位
        // 前面已经设置过
        const bool flushTransactions = clearTransactionFlags(eTransactionFlushNeeded);
        
        frontend::Update updates;
        if (flushTransactions) {
            // 1. 收集需要更新的数据,例如 transcation
            // 返回的 updates 保存了需要更新的数据
            updates = flushLifecycleUpdates();
            // ...
        }
        
        bool transactionsAreEmpty;
        if (mLegacyFrontEndEnabled) {
            // 2. 处理 updates 中的数据。
            // 这里会 apply transcation 和 commit transaction
            mustComposite |= updateLayerSnapshotsLegacy(vsyncId, updates, flushTransactions,
                                                        transactionsAreEmpty);
        }
        
        // ...
    }

    // ...

    // 3.把窗口信息,焦点窗口请求,更新到 InputFlinger
    updateInputFlinger(vsyncId, frameTime);

    // ...

    return mustComposite && CC_LIKELY(mBootStage != BootStage::BOOTLOADER);
}

这里简单过过一下 SurfaceFlinger 处理 transaction 过程,首先它会通过 flushLifecycleUpdates() 把需要更新的数据,收集到 frontend::Update。这些数据就保存 apply 的 transaction,如下

cpp 复制代码
// SurfaceFlinger.cpp

frontend::Update SurfaceFlinger::flushLifecycleUpdates() {
    frontend::Update update;
    ATRACE_NAME("TransactionHandler:flushTransactions");
    
    // 收集 TransactionHandler:: mLocklessTransactionQueue 保存的 transaction
    update.transactions = mTransactionHandler.flushTransactions();
    {
        // 这些收集的数据,与本文分析无关
        std::scoped_lock<std::mutex> lock(mCreatedLayersLock);
        update.layerCreatedStates = std::move(mCreatedLayers);
        mCreatedLayers.clear();
        update.newLayers = std::move(mNewLayers);
        mNewLayers.clear();
        update.layerCreationArgs = std::move(mNewLayerArgs);
        mNewLayerArgs.clear();
        update.destroyedHandles = std::move(mDestroyedHandles);
        mDestroyedHandles.clear();
    }
    return update;
}

数据收集到 frontend::Update 后,开始处理这个数据,其中就包括 transcation

cpp 复制代码
// SurfaceFlinger.cpp

bool SurfaceFlinger::updateLayerSnapshotsLegacy(VsyncId vsyncId, frontend::Update& update,
                                                bool transactionsFlushed,
                                                bool& outTransactionsAreEmpty) {
    bool needsTraversal = false;
    if (transactionsFlushed) {
        needsTraversal |= commitMirrorDisplays(vsyncId);
        needsTraversal |= commitCreatedLayers(vsyncId, update.layerCreatedStates);

        // 1. apply transaction 中的数据,
        // 例如,更新 layer 数据,用 mInputWindowCommands 保存焦点窗口请求
        needsTraversal |= applyTransactions(update.transactions, vsyncId);
    }
    
    outTransactionsAreEmpty = !needsTraversal;
    const bool shouldCommit = (getTransactionFlags() & ~eTransactionFlushNeeded) || needsTraversal;
    if (shouldCommit) {
        // 2.准备数据,用于合成
        commitTransactions();
    }

    bool mustComposite = latchBuffers() || shouldCommit;
    updateLayerGeometry();
    return mustComposite;
}

由于本文分析的 apply transaction,只包含需要更新的 layer,和焦点窗口请求,如下

cpp 复制代码
// SurfaceFlinger.cpp

bool SurfaceFlinger::applyTransactionState(const FrameTimelineInfo& frameTimelineInfo,
                                           std::vector<ResolvedComposerState>& states,
                                           Vector<DisplayState>& displays, uint32_t flags,
                                           const InputWindowCommands& inputWindowCommands,
                                           const int64_t desiredPresentTime, bool isAutoTimestamp,
                                           const std::vector<uint64_t>& uncacheBufferIds,
                                           const int64_t postTime, bool hasListenerCallbacks,
                                           const std::vector<ListenerCallbacks>& listenerCallbacks,
                                           int originPid, int originUid, uint64_t transactionId) {
    uint32_t transactionFlags = 0;
    if (!mLayerLifecycleManagerEnabled) {
        for (DisplayState& display : displays) {
            // 处理 display change
            transactionFlags |= setDisplayStateLocked(display);
        }
    }

    // start and end registration for listeners w/ no surface so they can get their callback.  Note
    // that listeners with SurfaceControls will start registration during setClientStateLocked
    // below.
    for (const auto& listener : listenerCallbacks) {
        mTransactionCallbackInvoker.addEmptyTransaction(listener);
    }

    uint32_t clientStateFlags = 0;
    for (auto& resolvedState : states) {
        if (mLegacyFrontEndEnabled) {
            // 1.处理 layer change
            clientStateFlags |=
                    setClientStateLocked(frameTimelineInfo, resolvedState, desiredPresentTime,
                                         isAutoTimestamp, postTime, transactionId);

        } else /*mLayerLifecycleManagerEnabled*/ {
            // ...
        }
        
        // ...
    }

    transactionFlags |= clientStateFlags;

    // 2. mInputWindowCommands 合并传入的参数 inputWindowCommands
    // inputWindowCommands 保存了焦点窗口请求
    transactionFlags |= addInputWindowCommands(inputWindowCommands);

    // ...

    return needsTraversal;
}


// 处理 layer change
uint32_t SurfaceFlinger::setClientStateLocked(const FrameTimelineInfo& frameTimelineInfo,
                                              ResolvedComposerState& composerState,
                                              int64_t desiredPresentTime, bool isAutoTimestamp,
                                              int64_t postTime, uint64_t transactionId) {
    // composerState 是要更新的 layer 数据
    layer_state_t& s = composerState.state;

    // ...

    const uint64_t what = s.what;
    uint32_t flags = 0;
    sp<Layer> layer = nullptr;
    if (s.surface) {
        // 获取 Layer,下面就开始更新 Layer 信息
        layer = LayerHandle::getLayer(s.surface);
    } else {
        // ...
    }
   
    // ..
    
    // 更新窗口信息时,设置过 eInputInfoChanged
    if (what & layer_state_t::eInputInfoChanged) {
        // 用 layer->mDrawingState.inputInfo 保存窗口信息
        layer->setInputInfo(*s.windowInfoHandle->getInfo());
        flags |= eTraversalNeeded;
    }
    // ...

    return flags;
}

现在,Layer 使用 mDrawingState.inputInfo 保存了输入窗口的信息,SurfaceFlinger 使用 mInputWindowCommands 保存了焦点窗口请求。现在需要把这些数据同步给 InputFlinger,如下

cpp 复制代码
// SurfaceFlinger.cpp

void SurfaceFlinger::updateInputFlinger(VsyncId vsyncId, TimePoint frameTime) {
    if (!mInputFlinger || (!mUpdateInputInfo && mInputWindowCommands.empty())) {
        return;
    }
    ATRACE_CALL();

    std::vector<WindowInfo> windowInfos;
    std::vector<DisplayInfo> displayInfos;
    bool updateWindowInfo = false;
    
    // Layer 保存窗口信息是,会把 mUpdateInputInfo 更新为 true
    if (mUpdateInputInfo) {
        mUpdateInputInfo = false;
        updateWindowInfo = true;
        // 把 window info 和 display info 分别填充到 windowInfos 到 displayInfos
        buildWindowInfos(windowInfos, displayInfos);
    }

    std::unordered_set<int32_t> visibleWindowIds;
    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);
    }

    // 后台线程执行 Callback
    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) {
            // 1. WindowInfosListenerInvoker 处理窗口信息改变
            mWindowInfosListenerInvoker
                    ->windowInfosChanged(gui::WindowInfosUpdate{std::move(windowInfos),
                                                                std::move(displayInfos),
                                                                vsyncId.value, frameTime.ns()},
                                         std::move(
                                                 inputWindowCommands.windowInfosReportedListeners),
                                         /* forceImmediateCall= */ visibleWindowsChanged ||
                                                 !inputWindowCommands.focusRequests.empty());
        } else {
            // ...
        }

        // 2. 把焦点窗口请求发送给 InputFlinger
        for (const auto& focusRequest : inputWindowCommands.focusRequests) {
            inputFlinger->setFocusedWindow(focusRequest);
        }
    }});

    mInputWindowCommands.clear();
}

InputDispatcher 更新窗口信息

WindowInfosListenerInvoker 管理所有对窗口感兴趣的监听者,它会把窗口信息发送给给监听者,如下

cpp 复制代码
// WindowInfosListenerInvoker.cpp

void WindowInfosListenerInvoker::windowInfosChanged(
        gui::WindowInfosUpdate update, WindowInfosReportedListenerSet reportedListeners,
        bool forceImmediateCall) {
    // ...

    // Call the listeners
    for (auto& pair : mWindowInfosListeners) {
        auto& [listenerId, listener] = pair.second;
        // 把窗口信息发送给 listener
        auto status = listener->onWindowInfosChanged(update);
        if (!status.isOk()) {
            ackWindowInfosReceived(update.vsyncId, listenerId);
        }
    }
}

InputDispatcher 就注册过一个 listener 如下

cpp 复制代码
// InputDispatcher.cpp

InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
                                 std::chrono::nanoseconds staleEventTimeout)
    // ...
{
    // ...

    mWindowInfoListener = sp<DispatcherWindowListener>::make(*this);
    SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
    
    // ..
}

现在看下 InputDispatcher 如何处理这些需要更新的窗口信息

cpp 复制代码
// InputDispatcher.cpp

void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(
        const gui::WindowInfosUpdate& update) {
    mDispatcher.onWindowInfosChanged(update);
}


void InputDispatcher::onWindowInfosChanged(const gui::WindowInfosUpdate& update) {

    // handlesPerDisplay 根据 display id,保存需要更新的窗口信息
    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);

        // Ensure that we have an entry created for all existing displays so that if a displayId has
        // no windows, we can tell that the windows were removed from the display.
        for (const auto& [displayId, _] : mWindowHandlesByDisplay) {
            handlesPerDisplay[displayId];
        }

        // 这是更新 display info
        mDisplayInfos.clear();
        for (const auto& displayInfo : update.displayInfos) {
            mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
        }

        // 这是更新 window info
        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();
}

这里直接看下 InputDispatcher 如何处理窗口信息更新

cpp 复制代码
// InputDispatcher.cpp

void InputDispatcher::setInputWindowsLocked(
        const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
    // 打开 DEBUG_FOCUS : adb shell setprop log.tag.InputDispatcherFocus DEBUG" ,然后重启
    if (DEBUG_FOCUS) {
        std::string windowList;
        for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) {
            windowList += iwh->getName() + " ";
        }
        ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
    }

    // ...

    // Copy old handles for release if they are no longer present.
    // 从 mWindowHandlesByDisplay 中,根据 display id,获取当前保存的所有窗口信息
    // 这些窗口信息,是旧的信息,因为马上要更新了
    const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);

    // >..

    // 根据 windowInfoHandles 中的窗口信息,更新 mWindowHandlesByDisplay 保存的窗口信息
    // 主要,并不是简单的移除和添加,而只是把窗口的数据更新
    updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);

    // 此时从 mWindowHandlesByDisplay 获取的就是更新后的窗口信息
    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);

    // 1.FocusResolver 保存更新后所有的窗口信息
    // 同时会根据这些窗口信息,以及焦点请求,来更新焦点窗口
    std::optional<FocusResolver::FocusChanges> changes =
            mFocusResolver.setInputWindows(displayId, windowHandles);

    // 2. 处理焦点窗口改变
    if (changes) {
        onFocusChangedLocked(*changes);
    }

    // ...
}

InputDispatcher 收到窗口数据后,更新了 mWindowHandlesByDisplay 保存的数据。然后,把最新的窗口数据发送给 FocusResolver ,让其解析最新的焦点窗口。最后,处理焦点窗口改变,其实就通知各方,焦点窗口改变,其中就包括上层 InputManagerService & WindowManagerService。

FocusResolver 更新焦点窗口

cpp 复制代码
// FocusResolver.cpp

std::optional<FocusResolver::FocusChanges> FocusResolver::setInputWindows(
        int32_t displayId, const std::vector<sp<WindowInfoHandle>>& windows) {
    std::string removeFocusReason;

    // 从 mFocusRequestByDisplay 根据 display id ,获取 focus request
    const std::optional<FocusRequest> request = getFocusRequest(displayId);
    // 从 mFocusedWindowTokenByDisplay 根据 display id,获取焦点窗口
    const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);

    // Find the next focused token based on the latest FocusRequest. If the requested focus window
    // cannot be focused, focus will be removed.
    if (request) {
        sp<IBinder> requestedFocus = request->token;
        sp<WindowInfoHandle> resolvedFocusWindow;

        // 1. 根据请求的焦点窗口,以及更新后的窗口信息,解析新的焦点窗口
        Focusability result = getResolvedFocusWindow(requestedFocus, windows, resolvedFocusWindow);

        if (result == Focusability::OK && resolvedFocusWindow->getToken() == currentFocus) {
            return std::nullopt;
        }

        // mLastFocusResultByDisplay 保存更新焦点窗口后的结果
        const Focusability previousResult = mLastFocusResultByDisplay[displayId];
        mLastFocusResultByDisplay[displayId] = result;

        if (result == Focusability::OK) {
            LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
                                "Focused window should be non-null when result is OK!");
            // 2. 找到了焦点窗口,用 mFocusedWindowTokenByDisplay 根据 display id 保存最新的焦点窗口
            return updateFocusedWindow(displayId,
                                       "Window became focusable. Previous reason: " +
                                               ftl::enum_string(previousResult),
                                       resolvedFocusWindow->getToken(),
                                       resolvedFocusWindow->getName());
        }
        removeFocusReason = ftl::enum_string(result);
    }

    // 3. 没找到焦点窗口,从 mFocusedWindowTokenByDisplay 移除焦点窗口数据
    // Focused window is no longer focusable and we don't have a suitable focus request to grant.
    // Remove focus if needed.
    return updateFocusedWindow(displayId, removeFocusReason, nullptr);
}

FocusResolver 获取最新的窗口数据后,再结合当前的焦点窗口请求(焦点窗口请求来自于 SurfaceFlinger),解析最新的焦点窗口,如下

cpp 复制代码
// FocusResolver.cpp

// token 为焦点窗口请求中的焦点窗口
// windows 为所有最新的窗口信息
// outFocusableWindow 保存解析后的焦点窗口
FocusResolver::Focusability FocusResolver::getResolvedFocusWindow(
        const sp<IBinder>& token, const std::vector<sp<WindowInfoHandle>>& windows,
        sp<WindowInfoHandle>& outFocusableWindow) {
    sp<IBinder> curFocusCandidate = token;
    bool focusedWindowFound = false;

    // Keep track of all windows reached to prevent a cyclical transferFocus request.
    std::unordered_set<sp<IBinder>, SpHash<IBinder>> tokensReached;

    while (curFocusCandidate != nullptr && tokensReached.count(curFocusCandidate) == 0) {
        tokensReached.emplace(curFocusCandidate);

        // 检测 焦点窗口请求中的焦点窗口 是否可以获得焦点
        // 原理是必须在 windows 中匹配到窗口,并且可见、可以获得焦点
        Focusability result = isTokenFocusable(curFocusCandidate, windows, outFocusableWindow);
        if (result == Focusability::OK) {
            LOG_ALWAYS_FATAL_IF(!outFocusableWindow,
                                "Focused window should be non-null when result is OK!");
            focusedWindowFound = true;
            // outFocusableWindow has been updated by isTokenFocusable to contain
            // the window info for curFocusCandidate. See if we can grant focus
            // to the token that it wants to transfer its focus to.
            curFocusCandidate = outFocusableWindow->getInfo()->focusTransferTarget;
        }

        // If the initial token is not focusable, return early with the failed result.
        if (!focusedWindowFound) {
            return result;
        }
    }

    return focusedWindowFound ? Focusability::OK : Focusability::NO_WINDOW;
}


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;
    for (const sp<WindowInfoHandle>& window : windows) {
        // token 是请求的焦点窗口
        // 这里很显然,是从所有窗口信息中,找到 token 对应的窗口
        if (window->getToken() != token) {
            continue;
        }

        // 这里表示 token 对应的窗口,是存在于 windows 中
        windowFound = true;
        
        // 检测窗口是否可见
        if (!window->getInfo()->inputConfig.test(gui::WindowInfo::InputConfig::NOT_VISIBLE)) {
            // Check if at least a single window is visible.
            visibleWindowHandle = window;
        }
        
        // 检测窗口是否可以获得焦点
        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;
    }

    // 找到了焦点窗口
    // Only set the outFoundWindow if the window can be focused
    outFocusableWindow = visibleWindowHandle;
    return Focusability::OK;
}

解析焦点窗口的原理是,根据焦点窗口请求的焦点窗口,在所有窗口信息中进行匹配,如果匹配到了,并且窗口可见、可以获得焦点,那么这个焦点请求就是有效的,并且还能得到焦点窗口。

得到焦点窗口后,FocusResolver 使用 mFocusedWindowTokenByDisplay ,根据 display id 进行保存,否则进行移除。

处理焦点窗口改变

cpp 复制代码
// 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);
            // 向 inbound queue 中添加一个类型为 FOCUS 的事件
            enqueueFocusEventLocked(changes.oldFocus, /*hasFocus=*/false, changes.reason);
        }
    }
    
    // 新焦点窗口存在
    if (changes.newFocus) {
        // 向 inbound queue 中添加一个类型为 FOCUS 事件
        enqueueFocusEventLocked(changes.newFocus, /*hasFocus=*/true, changes.reason);
    }

    // 针对 pointer capture 的
    disablePointerCaptureForcedLocked();

    // 如果焦点窗口的 display 没有改变
    if (mFocusedDisplayId == changes.displayId) {
        // 发送一个 focus change 命令,通知上层,焦点窗口改变了
        sendFocusChangedCommandLocked(changes.oldFocus, changes.newFocus);
    }
}

InputDispatcher 处理焦点窗口改变时,为新旧焦点窗口(必须存在),分别向 inbound queue 中添加了一个类型为 FOCUS 事件,如下

cpp 复制代码
// InputDispatcher.cpp

void InputDispatcher::enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
                                              const std::string& reason) {
    // 把 mPendingEvent 回退到 inbound queue 中,因为可以重新发送给新的焦点窗口
    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);

    // 从 mInboundQueue 中,反向查找 type 为 FOCUS 的 events 的 index
    // This event should go to the front of the queue, but behind all other focus events
    // Find the last focus event, and insert right after it
    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;
                         });

    // 把 focusEntry 插入到最后一个 type 为 FOCUS 的 event 之后
    // Maintain the order of focus events. Insert the entry after all other focus events.
    mInboundQueue.insert(it.base(), std::move(focusEntry));
}

这个类型为 FOCUS 的事件,指定发送给旧/新焦点窗口,如下

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 保存窗口信息
    InputTarget target;
    // channel 是旧焦点窗口,或者新焦点窗口
    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);
    // event log: 例如
    // input_focus: [Focus leaving f3b938f com.android.launcher3/com.android.searchlauncher.SearchLauncher (server),reason=NO_WINDOW]
    // input_focus: [Focus entering f87d90c com.awesome.helloworld/com.awesome.helloworld.MainActivity (server),reason=Window became focusable. Previous reason: NOT_VISIBLE]
    android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS;
    
    // 分发给旧/新焦点窗口
    // TODO: 最终效果是什么呢?
    dispatchEventLocked(currentTime, entry, {target});
}

但是,目前我还没研究上层收到这个焦点事件后,会有什么效果,如果有小伙伴知道,可以留言告诉我。

另外,如果焦点窗口的 display 没有改变,还会发送一个命令去通知上层,焦点窗口改变了,如下

cpp 复制代码
// InputDispatcher.cpp

void InputDispatcher::sendFocusChangedCommandLocked(const sp<IBinder>& oldToken,
                                                    const sp<IBinder>& newToken) {
    auto command = [this, oldToken, newToken]() REQUIRES(mLock) {
        scoped_unlock unlock(mLock);
        // 通知策略,焦点窗口改变
        mPolicy.notifyFocusChanged(oldToken, newToken);
    };
    
    // 发送一个命令
    postCommandLocked(std::move(command));
}

执行这个命令,最终会通知到上层,输入系统的焦点窗口,真的改变了

cpp 复制代码
// com_android_server_input_InputManagerService.cpp

void NativeInputManager::notifyFocusChanged(const sp<IBinder>& oldToken,
        const sp<IBinder>& newToken) {
#if DEBUG_INPUT_DISPATCHER_POLICY
    ALOGD("notifyFocusChanged");
#endif
    ATRACE_CALL();

    JNIEnv* env = jniEnv();
    ScopedLocalFrame localFrame(env);

    jobject oldTokenObj = javaObjectForIBinder(env, oldToken);
    jobject newTokenObj = javaObjectForIBinder(env, newToken);
    
    // 通知上层 InputManagerService
    env->CallVoidMethod(mServiceObj, gServiceClassInfo.notifyFocusChanged,
            oldTokenObj, newTokenObj);
    checkAndClearExceptionFromCallback(env, "notifyFocusChanged");
}
java 复制代码
// InputManagerService.java

// Native callback
@SuppressWarnings("unused")
private void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
    // mWindowManagerCallbacks 是由 WMS 注册的
    mWindowManagerCallbacks.notifyFocusChanged(oldToken, newToken);
}
java 复制代码
// InputManagerCallback.java

public void notifyFocusChanged(IBinder oldToken, IBinder newToken) {
    // 调用 reportFocusChanged()
    mService.mH.sendMessage(PooledLambda.obtainMessage(
            mService::reportFocusChanged, oldToken, newToken));
}
java 复制代码
// WindowManagerService.java

void reportFocusChanged(IBinder oldToken, IBinder newToken) {
    InputTarget lastTarget;
    InputTarget newTarget;
    synchronized (mGlobalLock) {
        lastTarget = getInputTargetFromToken(oldToken);
        newTarget = getInputTargetFromToken(newToken);
        if (newTarget == null && lastTarget == null) {
            Slog.v(TAG_WM, "Unknown focus tokens, dropping reportFocusChanged");
            return;
        }
        
        // mFocusedInputTarget 代表输入系统中最新的焦点窗口
        mFocusedInputTarget = newTarget;

        mAccessibilityController.onFocusChanged(lastTarget, newTarget);
        
        // 这个 log,代表 WMS 收到输入系统焦点窗口改变
        ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "Focus changing: %s -> %s", lastTarget, newTarget);
    }

    // Call WindowState focus change observers
    WindowState newFocusedWindow = newTarget != null ? newTarget.getWindowState() : null;
    if (newFocusedWindow != null && newFocusedWindow.mInputChannelToken == newToken) {
        // 取消 ANR dialog
        mAnrController.onFocusChanged(newFocusedWindow);
        
        // 目前没有发现下面两个方法有什么用
        newFocusedWindow.reportFocusChangedSerialized(true);
        notifyFocusChanged();
    }

    WindowState lastFocusedWindow = lastTarget != null ? lastTarget.getWindowState() : null;
    if (lastFocusedWindow != null && lastFocusedWindow.mInputChannelToken == oldToken) {
        lastFocusedWindow.reportFocusChangedSerialized(false);
    }
}

InputFlinger 下发焦点窗口请求

在输入系统中,InputFlinger 指的是 InputManager,因为它实现了 IInputFlinger 接口。所以,将由 InputManager 接收焦点窗口请求,如下

cpp 复制代码
// InputManager.cpp

binder::Status InputManager::setFocusedWindow(const FocusRequest& request) {
    // 交给 InputDispatcher 处理
    mDispatcher->setFocusedWindow(request);
    return binder::Status::ok();
}
cpp 复制代码
// InputDispatcher.cpp

void InputDispatcher::setFocusedWindow(const FocusRequest& request) {
    { // acquire lock
        std::scoped_lock _l(mLock);
        // 1. FocusResolver 处理焦点窗口请求
        std::optional<FocusResolver::FocusChanges> changes =
                mFocusResolver.setFocusedWindow(request, getWindowHandlesLocked(request.displayId));
        if (changes) {
            // 2. 处理焦点窗口改变
            onFocusChangedLocked(*changes);
        }
    } // release lock
    
    // 如果有事件因为没有焦点窗口而阻塞,此时唤醒线程就可以处理
    // Wake up poll loop since it may need to make new input dispatching choices.
    mLooper->wake();
}

第二步的处理焦点窗口改变,前面已经分析过,这里主要看下 FocusResolver 如何处理焦点窗口请求

cpp 复制代码
// FocusResolver.cpp

std::optional<FocusResolver::FocusChanges> FocusResolver::setFocusedWindow(
        const FocusRequest& request, const std::vector<sp<WindowInfoHandle>>& windows) {
    const int32_t displayId = request.displayId;
    
    // 从 mFocusedWindowTokenByDisplay,根据 display id 获取焦点窗口
    // 这是旧焦点窗口请求
    const sp<IBinder> currentFocus = getFocusedWindowToken(displayId);
    
    if (currentFocus == request.token) {
        ALOGD_IF(DEBUG_FOCUS,
                 "setFocusedWindow %s on display %" PRId32 " ignored, reason: already focused",
                 request.windowName.c_str(), displayId);
        return std::nullopt;
    }

    sp<WindowInfoHandle> resolvedFocusWindow;
    
    // 根据最新的焦点窗口请求,解析焦点窗口
    Focusability result = getResolvedFocusWindow(request.token, windows, resolvedFocusWindow);
    
    // Update focus request. The focus resolver will always try to handle this request if there is
    // no focused window on the display.
    mFocusRequestByDisplay[displayId] = request;
    mLastFocusResultByDisplay[displayId] = result;

    // 如果成功解析出焦点窗口,用 mFocusedWindowTokenByDisplay 以 display id 为 KEY,保存焦点窗口
    if (result == Focusability::OK) {
        LOG_ALWAYS_FATAL_IF(!resolvedFocusWindow,
                            "Focused window should be non-null when result is OK!");
        return updateFocusedWindow(displayId, "setFocusedWindow", resolvedFocusWindow->getToken(),
                                   resolvedFocusWindow->getName());
    }

    // 如果没有解析出焦点窗口,从 mFocusedWindowTokenByDisplay 移除焦点窗口
    return updateFocusedWindow(displayId, "Waiting for window because " + ftl::enum_string(result),
                               nullptr);
}

根据焦点窗口请求,来解析焦点窗口,前面已经分析过。得到焦点窗口后,根据 display id 保存到 mFocusedWindowTokenByDisplay 中。

更新焦点 app

无焦点窗口 ANR ,除了与交焦点窗口有关,还与焦点 app (实际为 ActivityRecord)有关。

当服务端通知 app 端去 resume activity 后,把这个 activity 作为焦点 app,发送给底层,如下

java 复制代码
// DisplayContent.java

// newFocus 为焦点 app
boolean setFocusedApp(ActivityRecord newFocus) {
    if (newFocus != null) {
        final DisplayContent appDisplay = newFocus.getDisplayContent();
        if (appDisplay != this) {
            throw new IllegalStateException(newFocus + " is not on " + getName()
                    + " but " + ((appDisplay != null) ? appDisplay.getName() : "none"));
        }

        // Called even if the focused app is not changed in case the app is moved to a different
        // TaskDisplayArea.
        onLastFocusedTaskDisplayAreaChanged(newFocus.getDisplayArea());
    }
    
    // 焦点 app 必须改变
    if (mFocusedApp == newFocus) {
        return false;
    }
    
    
    ProtoLog.i(WM_DEBUG_FOCUS_LIGHT, "setFocusedApp %s displayId=%d Callers=%s",
            newFocus, getDisplayId(), Debug.getCallers(4));
            
    final Task oldTask = mFocusedApp != null ? mFocusedApp.getTask() : null;
    final Task newTask = newFocus != null ? newFocus.getTask() : null;
    
    // DisplayContent 使用 mFocusedApp 保存焦点 app
    mFocusedApp = newFocus;
    
    if (oldTask != newTask) {
        if (oldTask != null) oldTask.onAppFocusChanged(false);
        if (newTask != null) newTask.onAppFocusChanged(true);
    }

    // 设置焦点 app
    getInputMonitor().setFocusedAppLw(newFocus);
    return true;
}


void setFocusedAppLw(ActivityRecord newApp) {
    // 向输入系统设置焦点 app
    mService.mInputManager.setFocusedApplication(mDisplayId,
            newApp != null ? newApp.getInputApplicationHandle(true /* update */) : null);
}
java 复制代码
// InputManagerService.java

public void setFocusedApplication(int displayId, InputApplicationHandle application) {
    mNative.setFocusedApplication(displayId, application);
}
cpp 复制代码
// com_android_server_input_InputManagerService.cpp

void NativeInputManager::setFocusedApplication(JNIEnv* env, ui::LogicalDisplayId displayId,
                                               jobject applicationHandleObj) {
    if (!applicationHandleObj) {
        return;
    }
    
    // 转化为 native 层的 InputApplicationHandle
    std::shared_ptr<InputApplicationHandle> applicationHandle =
            android_view_InputApplicationHandle_getHandle(env, applicationHandleObj);
    applicationHandle->updateInfo();
    
    // 焦点 app 直接发送给 InputDispatcher
    mInputManager->getDispatcher().setFocusedApplication(displayId, applicationHandle);
}
cpp 复制代码
// InputDispatcher.cpp

void InputDispatcher::setFocusedApplication(
        int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
    
    // 执行命令 adb shell setprop log.tag.InputDispatcherFocus DEBUG,然后重启
    // 就可以打开 DEBUG_FOCUS 开关
    if (DEBUG_FOCUS) {
        ALOGD("setFocusedApplication displayId=%" PRId32 " %s", displayId,
              inputApplicationHandle ? inputApplicationHandle->getName().c_str() : "<nullptr>");
    }
    
    { // acquire lock
        std::scoped_lock _l(mLock);
        // 设置焦点 app
        setFocusedApplicationLocked(displayId, inputApplicationHandle);
    } // release lock

    // 唤醒线程
    // 因为有的事件还在等待焦点 app,才能分发
    // Wake up poll loop since it may need to make new input dispatching choices.
    mLooper->wake();
}


void InputDispatcher::setFocusedApplicationLocked(
        int32_t displayId, const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle) {
        
    // 从 mFocusedApplicationHandlesByDisplay 根据 display id 获取焦点 app
    std::shared_ptr<InputApplicationHandle> oldFocusedApplicationHandle =
            getValueByKey(mFocusedApplicationHandlesByDisplay, displayId);

    // 这里是检测焦点app是否改变
    if (sharedPointersEqual(oldFocusedApplicationHandle, inputApplicationHandle)) {
        return; // This application is already focused. No need to wake up or change anything.
    }

    if (inputApplicationHandle != nullptr) {
        // mFocusedApplicationHandlesByDisplay 根据 display id 保存焦点 app
        mFocusedApplicationHandlesByDisplay[displayId] = inputApplicationHandle;
    } else {
        // ...
    }

    // 如果有事件正在等待一个旧的焦点 app,而此时焦点 app 改变,因此需要重置
    // No matter what the old focused application was, stop waiting on it because it is
    // no longer focused.
    resetNoFocusedWindowTimeoutLocked();
}

焦点app,最终由 InputDispater 的 mFocusedApplicationHandlesByDisplay,以 display id 为 KEY 进行保存。

相关推荐
django-尿素12 分钟前
django入门-orm数据库操作
android·数据库·django
封妖九禁苍天泣17 分钟前
Glide NoResultEncoderAvailableException异常解决
android
Yusei_05232 小时前
C++ 模版复习
android·java·c++
puffysang333 小时前
Android 编译FFmpeg4.3.1并集成x264
android
whysqwhw3 小时前
Transcoder代码学习
android
雨白3 小时前
详解 RecyclerView:从基础到布局与点击事件
android
开开心心_Every4 小时前
免费且好用的PDF水印添加工具
android·javascript·windows·智能手机·pdf·c#·娱乐
张风捷特烈4 小时前
每日一题 Flutter#2 | 如何理解 Widget 的不可变性
android·flutter·面试
一起搞IT吧4 小时前
相机Camera日志分析之二十四:高通相机Camx 基于预览1帧的process_capture_request三级日志分析详解
android·图像处理·数码相机
小鱼人爱编程4 小时前
进入外包,我犯了所有程序员都会犯的错!
android·前端·程序员