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 进行保存。

相关推荐
小蜜蜂嗡嗡1 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi001 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体
zhangphil3 小时前
Android理解onTrimMemory中ComponentCallbacks2的内存警戒水位线值
android
你过来啊你3 小时前
Android View的绘制原理详解
android
移动开发者1号6 小时前
使用 Android App Bundle 极致压缩应用体积
android·kotlin
移动开发者1号6 小时前
构建高可用线上性能监控体系:从原理到实战
android·kotlin
ii_best11 小时前
按键精灵支持安卓14、15系统,兼容64位环境开发辅助工具
android
美狐美颜sdk11 小时前
跨平台直播美颜SDK集成实录:Android/iOS如何适配贴纸功能
android·人工智能·ios·架构·音视频·美颜sdk·第三方美颜sdk
恋猫de小郭15 小时前
Meta 宣布加入 Kotlin 基金会,将为 Kotlin 和 Android 生态提供全新支持
android·开发语言·ios·kotlin
aqi0016 小时前
FFmpeg开发笔记(七十七)Android的开源音视频剪辑框架RxFFmpeg
android·ffmpeg·音视频·流媒体