Android R WindowManagerService模块(5) 焦点窗口和InputWindows的更新

Android R WindowManagerService模块(5) 焦点窗口和InputWindows的更新

Android小彩虹2021-07-08 13:24:28140A+A-

这篇文章中对焦点窗口的进行下分析总结。

焦点窗口是指当前正在与用户交互的窗口,该窗口负责接收键事件和触摸事件。当启动新的Activity、添加新的窗口、移除旧窗口、分屏来回操作时,都会涉及到焦点窗口的更新。

可以通过如下shell命令来查看当前的FocusWindow:

$ adb shell dumpsys window d | grep "mCurrentFocus"
  mCurrentFocus=Window{135c912 mode=0 rootTaskId=4 u0 com.example.myapplication/com.example.myapplication.MainActivity}

或者查看不同屏幕的FocusWindow:

$ adb shell dumpsys window d | grep mFocusedWindow
    mFocusedWindow=Window{7dbc553 mode=0 rootTaskId=60 u0 com.android.camera/com.android.camera.Camera}

InputWindow是指能接收input事件的窗口,当WMS中状态发生变化后,会将所有符合条件的窗口设置给底层InputFlinger中,在派发事件时,将对从这些窗口中选择目标窗口进行派发,这些窗口就是InputWindow。焦点窗口只有一个,但InputWindow可以有多个。

1. Focused Window的更新

焦点窗口的更新,通过WMS#updateFocusedWindowLocked()方法开始,下面从这个方法开始,看下整个更新流程。

1.1.WMS#updateFocusedWindowLocked()

该方法如下:

// frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        // 进入RootWindowContainer中
        boolean changed = mRoot.updateFocusedWindowLocked(mode, updateInputWindows);
        return changed;
    }

其中,第一个参数表示更新焦点窗口时所处的阶段,共有五个参数:

    // 表示正常更新
    static final int UPDATE_FOCUS_NORMAL = 0;
    // 表示此次更新焦点窗口发生在window layer分配之前
    static final int UPDATE_FOCUS_WILL_ASSIGN_LAYERS = 1;
    // 表示此次更新焦点窗口发生在进行放置Surface过程中,在performSurfacePlacement()时
    static final int UPDATE_FOCUS_PLACING_SURFACES = 2;
    // 表示此次更新焦点窗口发生在进行放置Surface之前
    static final int UPDATE_FOCUS_WILL_PLACE_SURFACES = 3;
    // 表示此次更新焦点窗口发生在焦点窗口移除后
    static final int UPDATE_FOCUS_REMOVING_FOCUS = 4;

如在Window添加过程的WMS#addWindow()方法中,更新焦点窗口发生在分配窗口layer流程之前,因此使用UPDATE_FOCUS_WILL_ASSIGN_LAYERS作为第一个参数,表示此次更新时,还没有分配layer。 针对不同阶段,会有不同的操作。

第二个参数表示是否同步更新InputWindow。

1.2.RWC#updateFocusedWindowLocked()

WMS中发起更新焦点窗口后,由RootWindowContainer中处理:

// frameworks/base/services/core/java/com/android/server/wm/RootWindowContainer.java

    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows) {
        // 存储了当前焦点窗口的Pid和ActivityRecord的Map映射
        mTopFocusedAppByProcess.clear();
        boolean changed = false;
        int topFocusedDisplayId = INVALID_DISPLAY;
        // 遍历DisplayContent
        for (int i = mChildren.size() - 1; i >= 0; --i) {
            final DisplayContent dc = mChildren.get(i);
            // 针对每个DisplayContent,进行焦点窗口更新
            changed |= dc.updateFocusedWindowLocked(mode, updateInputWindows, topFocusedDisplayId);
            // 更新全局焦点窗口
            final WindowState newFocus = dc.mCurrentFocus;
            if (newFocus != null) {
                final int pidOfNewFocus = newFocus.mSession.mPid;
                if (mTopFocusedAppByProcess.get(pidOfNewFocus) == null) {
                    mTopFocusedAppByProcess.put(pidOfNewFocus, newFocus.mActivityRecord);
                }
            } else if (topFocusedDisplayId == INVALID_DISPLAY && dc.mFocusedApp != null) {
                topFocusedDisplayId = dc.getDisplayId();
            }
        }
        // 更新mTopFocusedDisplayId,表示焦点屏幕
        if (mTopFocusedDisplayId != topFocusedDisplayId) {
            mTopFocusedDisplayId = topFocusedDisplayId;
            mWmService.mInputManager.setFocusedDisplay(topFocusedDisplayId);
            mWmService.mPolicy.setTopFocusedDisplay(topFocusedDisplayId);
        }
        return changed;
    }

这里会遍历DisplayContent,并在每个DisplayContent中进行更新,然后将更新的结果返回给DisplayContent#mCurrentFocus变量,该变量表示全局的焦点窗口。同时更新mTopFocusedDisplayId变量,表示当前焦点屏(即焦点窗口所在的屏)。

1.3.DisplayContent#updateFocusedWindowLocked()

该方法如下:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    boolean updateFocusedWindowLocked(int mode, boolean updateInputWindows, int topFocusedDisplayId) {
	// 寻找焦点窗口
        WindowState newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
	// 焦点窗口没有变化时,返回false
        if (mCurrentFocus == newFocus) {
            return false;
        }
        boolean imWindowChanged = false;
        final WindowState imWindow = mInputMethodWindow;
	// 更新IME target窗口
        if (imWindow != null) {
            // 记录当前IME Target窗口
            final WindowState prevTarget = mInputMethodTarget;
            // 获取新的IME Target窗口
            final WindowState newTarget = computeImeTarget(true /* updateImeTarget*/);
            // IME Target窗口是否发生变化
            imWindowChanged = prevTarget != newTarget;
            // 进行window layer的分配
            if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS
                    && mode != UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                assignWindowLayers(false /* setLayoutNeeded */);
            }
        }
	// IME target窗口发生变化,重新获取一次焦点窗口
        if (imWindowChanged) {
            mWmService.mWindowsChanged = true;
            setLayoutNeeded();
            newFocus = findFocusedWindowIfNeeded(topFocusedDisplayId);
        }
	// 通知WMS焦点窗口发生变化
        if (mCurrentFocus != newFocus) {
            mWmService.mH.obtainMessage(REPORT_FOCUS_CHANGE, this).sendToTarget();
        }
	// 记录旧焦点窗口
        final WindowState oldFocus = mCurrentFocus;
	// 更新新焦点窗口
        mCurrentFocus = newFocus;
	// 从mLosingFocus移除
        mLosingFocus.remove(newFocus);
		
        if (newFocus != null) {
            // 这俩列表用于记录ANR状态,表示自从焦点窗口为空时添加和移除的窗口
            mWinAddedSinceNullFocus.clear();
            mWinRemovedSinceNullFocus.clear();
            // 设置焦点窗口所在mToken.paused属性为false
            if (newFocus.canReceiveKeys()) {
                newFocus.mToken.paused = false;
            }
        }
	// 用于通知Task更新shadow radius
        onWindowFocusChanged(oldFocus, newFocus);
	// 通知DisplayPolicy焦点窗口发生变化,并接受返回结果
        int focusChanged = getDisplayPolicy().focusChangedLw(oldFocus, newFocus);
	// IME target窗口发生变化,且旧焦点窗口非输入法窗口时
        if (imWindowChanged && oldFocus != mInputMethodWindow) {
            // 根据mode, 进行layout或assign window layer操作
            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                performLayout(true /*initial*/,  updateInputWindows);
                focusChanged &= ~FINISH_LAYOUT_REDO_LAYOUT;
            } else if (mode == UPDATE_FOCUS_WILL_PLACE_SURFACES) {
                assignWindowLayers(false /* setLayoutNeeded */);
            }
        }
	// DislayPolicy返回结果
        if ((focusChanged & FINISH_LAYOUT_REDO_LAYOUT) != 0) {
            setLayoutNeeded();
            if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
                performLayout(true /*initial*/, updateInputWindows);
            } else if (mode == UPDATE_FOCUS_REMOVING_FOCUS) {
                mWmService.mRoot.performSurfacePlacement();
            }
        }
	// 向InputMonitor中设置焦点窗口
        if (mode != UPDATE_FOCUS_WILL_ASSIGN_LAYERS) {
            getInputMonitor().setInputFocusLw(newFocus, updateInputWindows);
        }
	// 为IME窗口进行调整
        adjustForImeIfNeeded();
	// Toast窗口
        scheduleToastWindowsTimeoutIfNeededLocked(oldFocus, newFocus);

        if (mode == UPDATE_FOCUS_PLACING_SURFACES) {
            pendingLayoutChanges |= FINISH_LAYOUT_REDO_ANIM;
        }
        return true;
    }

以上方法中:

    1. 通过findFocusedWindowIfNeeded()方法寻找焦点窗口;
    1. 根据焦点窗口的变化,更新Input Target窗口;
    1. 更新全局焦点窗口对象mCurrentFocus;
    1. 根据更新的不同阶段做不同处理。

下面看下如何寻找到焦点窗口。

1.4.DisplayContent#findFocusedWindowIfNeeded()

在这个方法中,会寻找到新的焦点窗口:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

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

当topFocusedDisplayId为INVALID_DISPLAY时,认为当前焦点display没有焦点窗口,需要寻找重新确认,所以又继续执行findFocusedWindow()方法寻找:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    WindowState findFocusedWindow() {
        mTmpWindow = null;
	// 遍历WindowState
        forAllWindows(mFindFocusedWindow, true /* traverseTopToBottom */);

        if (mTmpWindow == null) {
            return null;
        }
        return mTmpWindow;
    }

该方法中,会遍历所有WindowState,然后将寻找到的WindowState赋值给mTmpWindow,并返回给WMS。接下来看下这个mFindFocusedWindow函数接口对象:

// frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

    private final ToBooleanFunction<WindowState> mFindFocusedWindow = w -> {
	// 当前处于前台的ActivityRecord
        final ActivityRecord focusedApp = mFocusedApp;
	// 如果窗口无法接收key事件,则不能作为焦点窗口,返回false
        if (!w.canReceiveKeys()) {
            return false;
        }

        final ActivityRecord activity = w.mActivityRecord;
	// 如果前台没有Activity,则此次WindowState将作为焦点窗口返回
        if (focusedApp == null) {
            mTmpWindow = w;
            return true;
        }
	// 如果前台Activity是不可获焦的,则此次WindowState将作为焦点窗口返回
        if (!focusedApp.windowsAreFocusable()) {
            mTmpWindow = w;
            return true;
        }
	// 如果当前WindowState由ActivityRecord管理,且非StartingWindow,则当
        if (activity != null && w.mAttrs.type != TYPE_APPLICATION_STARTING) {
            if (focusedApp.compareTo(activity) > 0) {
                mTmpWindow = null;
                return true;
            }
        }
	// 不满足以上条件,则此次WindowState将作为焦点窗口返回
        mTmpWindow = w;
        return true;
    };

该方法中,将依次根据如下条件获得焦点窗口:

    1. 如果WindowState不能接收Input事件,则不能作为焦点窗口;
    1. 如果没有前台Activity,则当前WindowState作为焦点窗口返回;
    1. 如果前台Activity是不可获焦状态,则当前WindowState作为焦点窗口返回;
    1. 如果当前WindowState由ActivityRecord管理,且该WindowState不是Staring Window类型,那么当前台Activity在当前WindowState所属Activity之上时,不存在焦点窗口;
    1. 如果以上条件都不满足,则当前WindowState作为焦点窗口返回;

1.3.1.WindowState能否接受Input事件

这里接着看下第一个条件,判断窗口能否可以接受Input事件,由WindowState#canReceiveKeys()方法进行判断:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    public boolean canReceiveKeys(boolean fromUserTouch) {
        // 可见或处于addWindow()~relayout之间
        final boolean canReceiveKeys = isVisibleOrAdding() 
                // 客户端View可见
                && (mViewVisibility == View.VISIBLE)
                // 退出动画执行完毕
                && !mRemoveOnExit
                // 没有FLAG_NOT_FOCUSABLE标记
                && ((mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE) == 0) 
                // mActivityRecord为null或者其可获焦
                && (mActivityRecord == null || mActivityRecord.windowsAreFocusable(fromUserTouch)) 
                // 可以接受touch事件
                && !cantReceiveTouchInput();             
        if (!canReceiveKeys) {
            return false;
        }
        return fromUserTouch || getDisplayContent().isOnTop()
                || getDisplayContent().isTrusted();
    }

如果一个WindowState可以接受Input事件,需要同时满足多个条件:

    1. isVisibleOrAdding()方法为true,表示该WindowState可见或处于添加过程中:
    1. mViewVisibility属性为View.VISIBLE,表示客户端View可见;
    1. mRemoveOnExit为false,表示WindowState的退出动画不存在;
    1. mAttrs.flags中不存在FLAG_NOT_FOCUSABLE标记,该标记如果设置,表示该窗口为不可获焦窗口;
    1. mActivityRecord为null或者mActivityRecord可获焦;
    1. cantReceiveTouchInput()方法为false,表示可以接受Touch事件。

isVisibleOrAdding()方法用来判断该WindowState可见或处于添加过程中:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    boolean isVisibleOrAdding() {
        final ActivityRecord atoken = mActivityRecord;
        // Surface已经创建,说明relayout()已经执行
        return (mHasSurface 
                // relayout()未执行,且客户端View可见状态
                || (!mRelayoutCalled && mViewVisibility == View.VISIBLE))
                // 表示该WindowState由policy控制可见性的flag全部设置
                && isVisibleByPolicy() 
                // 如果是子窗口,其副窗口的mHidden属性为false
                && !isParentWindowHidden()
                // mActivityRecord为null,或者其mVisibleRequested为true
                && (atoken == null || atoken.mVisibleRequested)
                // 没有执行退出动画
                && !mAnimatingExit 
                // Surface没有进行销毁
                && !mDestroying;
    }

如果以上条件都满足,则返回true。

cantReceiveTouchInput()方法用来判断该WindowState是否能够接收touch事件:

// frameworks/base/services/core/java/com/android/server/wm/WindowState.java

    boolean cantReceiveTouchInput() {
        // 如果mActivityRecord为null,或mActivityRecord所在Task为null,返回false
        if (mActivityRecord == null || mActivityRecord.getTask() == null) {
            return false;
        }
        // 所在Task是否忽略输入事件
        return mActivityRecord.getTask().getStack().shouldIgnoreInput()
                // mActivityRecord.mVisibleRequested属性为false
                || !mActivityRecord.mVisibleRequested
                // 最近任务动画在消费Input事件
                || isRecentsAnimationConsumingAppInput();
    }

回到mFindFocusedWindow接口对象中,其他条件就不一一说明,最终从DisplayContent中自顶向下寻找到的第一个满足条件的窗口,将作为新的焦点窗口后,接下来更新Display#mCurrentFocus变量,表示当前焦点窗口。

2.InputWindows的更新

InputWindow的更新,是通过InputMonitor#updateInputWindowsLw()方法来进行。在焦点窗口的更新过程以及窗口的添加过程中,都会通过InputMonitor#setInputFocusLw()方法将新的焦点窗口同步给InputMonitor,并开始更新Input Windows。下面从这个方法开始看下Input windows的更新过程。

2.1.InputMonitor#updateInputWindowsLw()

该方法中,会将新的焦点窗口同步给InputMonitor:

// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

    public void setInputFocusLw(WindowState newWindow, boolean updateInputWindows) {
        if (newWindow != mInputFocus) {
            if (newWindow != null && newWindow.canReceiveKeys()) {
                newWindow.mToken.paused = false;
            }
            // 更新当前焦点窗口
            mInputFocus = newWindow;
            setUpdateInputWindowsNeededLw();
            // 更新所有inputwindow
            if (updateInputWindows) {
                updateInputWindowsLw(false /*force*/);
            }
        }
    }

之后将执行updateInputWindowsLw()方法来完成所有InputWindows的更新,该方法的执行时机非常多。

2.2.InputMonitor#updateInputWindowsLw()

在该方法中,只有当强制更新或者mUpdateInputWindowsNeeded值为true时,才会进行InputWindows的更新:

// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

    void updateInputWindowsLw(boolean force) {
        if (!force && !mUpdateInputWindowsNeeded) {
            return;
        }
        scheduleUpdateInputWindows();
    }

mUpdateInputWindowsNeeded变量表示是否需要更新InputWindows,当需要更新时,会通过setUpdateInputWindowsNeededLw()将该变量值设置为true:

// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

    void setUpdateInputWindowsNeededLw() {
        mUpdateInputWindowsNeeded = true;
    }

之后将执行scheduleUpdateInputWindows()方法,在这个方法中,会post一个Runnable对象,在mHandler所在的android.anim线程中执行更新流程:

// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

    private class UpdateInputWindows implements Runnable {
        @Override
        public void run() {
            synchronized (mService.mGlobalLock) {
                // 重置
                mUpdateInputWindowsPending = false;
                mUpdateInputWindowsNeeded = false;
                
                // 是否正在拖拽
                final boolean inDrag = mService.mDragDropController.dragDropActiveLocked();
                
                mUpdateInputForAllWindowsConsumer.updateInputWindows(inDrag);
            }
        }
    }

接下来执行mUpdateInputForAllWindowsConsumer.updateInputWindows()方法。

2.3.mUpdateInputForAllWindowsConsumer.updateInputWindows()

在该方法中,首先会确认是否存在几类特殊的InputConsumer。InputConsumer用于读取事件,每个窗口对应的客户端都会通过InputConsumer来读取和消费事件,一般情况下,ViewRootImpl在添加窗口过程中,会在注册InputEventReceiver时自动创建InputConsumer对象。此处的四类特殊InputConsumer则是对一些系统UI显式地进行了创建:

// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

        private void updateInputWindows(boolean inDrag) {
            // 显式创建的特殊InputConsumer对象
            // 用于处理Nav相关input事件
            mNavInputConsumer = getInputConsumer(INPUT_CONSUMER_NAVIGATION);
            // 用于处理Pip相关input事件
            mPipInputConsumer = getInputConsumer(INPUT_CONSUMER_PIP);
            // 用于处理壁纸相关input事件
            mWallpaperInputConsumer = getInputConsumer(INPUT_CONSUMER_WALLPAPER);
            // 用于处理最近任务相关input事件
            mRecentsAnimationInputConsumer = getInputConsumer(INPUT_CONSUMER_RECENTS_ANIMATION);

            mAddRecentsAnimationInputConsumerHandle = mRecentsAnimationInputConsumer != null;

            mTmpRect.setEmpty();
            mDisableWallpaperTouchEvents = false;
            mInDrag = inDrag;

            mWallpaperController = mDisplayContent.mWallpaperController;
            // 重置mInputTransaction
            resetInputConsumers(mInputTransaction);

            // 遍历
            mDisplayContent.forAllWindows(this,
                    true /* traverseTopToBottom */);
            // 将mInputTransaction合并到mPendingTransaction上进行提交
            if (!mUpdateInputWindowsImmediately) {
                mDisplayContent.getPendingTransaction().merge(mInputTransaction);
                mDisplayContent.scheduleAnimation();
            }

        }

然后,将发起所有WindowState的遍历,mUpdateInputForAllWindowsConsumer本身是一个Consumer接口对象,因此会回调accept()方法对每个WindowState进行处理:

// frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

        @Override
        public void accept(WindowState w) {
            // 获取WindowState的InputChannel对象
            final InputChannel inputChannel = w.mInputChannel;
            // 获取WindowState的InputWindowHandle对象
            final InputWindowHandle inputWindowHandle = w.mInputWindowHandle;
            // 最近任务是否存在
            final RecentsAnimationController recentsAnimationController =
                    mService.getRecentsAnimationController();
            final boolean shouldApplyRecentsInputConsumer = recentsAnimationController != null
                    && recentsAnimationController.shouldApplyInputConsumer(w.mActivityRecord);
            final int type = w.mAttrs.type;
            
            ......

            final int flags = w.mAttrs.flags;
            final int privateFlags = w.mAttrs.privateFlags;
            // 是否是焦点窗口
            final boolean hasFocus = w.isFocused();
            // mRecentsAnimationInputConsumer处理最近任务相关input事件
            if (mAddRecentsAnimationInputConsumerHandle && shouldApplyRecentsInputConsumer) {
                if (recentsAnimationController.updateInputConsumerForApp(
                        mRecentsAnimationInputConsumer.mWindowHandle, hasFocus)) {
                    mRecentsAnimationInputConsumer.show(mInputTransaction, w);
                    mAddRecentsAnimationInputConsumerHandle = false;
                }
            }

            // 处理处于PIP模式时的input事件
            if (w.inPinnedWindowingMode()) {
                
            }
            // mNavInputConsumer处理Nav相关input事件
            if (mAddNavInputConsumerHandle) {
                mNavInputConsumer.show(mInputTransaction, w);
                mAddNavInputConsumerHandle = false;
            }
            
            // mWallpaperInputConsumer处理壁纸input事件
            if (mAddWallpaperInputConsumerHandle) {
                if (w.mAttrs.type == TYPE_WALLPAPER && w.isVisibleLw()) {
                    mWallpaperInputConsumer.show(mInputTransaction, w);
                    mAddWallpaperInputConsumerHandle = false;
                }
            }
            // 是否壁纸不接收input事件
            if ((privateFlags & PRIVATE_FLAG_DISABLE_WALLPAPER_TOUCH_EVENTS) != 0) {
                mDisableWallpaperTouchEvents = true;
            }
            final boolean hasWallpaper = mWallpaperController.isWallpaperTarget(w)
                    && !mService.mPolicy.isKeyguardShowing()
                    && !mDisableWallpaperTouchEvents;
            // 是否处于拖拽过程中
            if (mInDrag && isVisible && w.getDisplayContent().isDefaultDisplay) {
                mService.mDragDropController.sendDragStartedIfNeededLocked(w);
            }
            // 填充InputWindowHandle
            populateInputWindowHandle(
                    inputWindowHandle, w, flags, type, isVisible, hasFocus, hasWallpaper);

            // 提交inputWindowHandle
            if (w.mWinAnimator.hasSurface()) {
                mInputTransaction.setInputWindowInfo(
                    w.mWinAnimator.mSurfaceController.getClientViewRootSurface(),
                    inputWindowHandle);
            }
        }

以上方法中:

  1. 首先会对几类特殊InputConsumer进行单独处理;
  2. 然后填充InputWindowHandle对象;
  3. 最后将InputWindowHandle对象设置给Transaction对象,并在事物提交后,由SurfaceFlinger设置给InputDispatcher中。

InputWindowHandle代表了WindowState,传给了InputDispatcher中用于派发事件,当InputDispatcher中进行事件的派发时,将以InputWindowHandle确定需要派发给哪些窗口。下面来看下对inputWindowHandle对象的填充。

2.4.InputMonitor#populateInputWindowHandle()

在该方法中,会将WindowState的部分属性填充给inputWindowHandle:

//frameworks/base/services/core/java/com/android/server/wm/InputMonitor.java

    void populateInputWindowHandle(final InputWindowHandle inputWindowHandle, final WindowState child, int flags, final int type, final boolean isVisible, final boolean hasFocus, final boolean hasWallpaper) {
        inputWindowHandle.name = child.toString();
        inputWindowHandle.inputApplicationHandle = child.mActivityRecord != null
                ? child.mActivityRecord.getInputApplicationHandle(false /* update */) : null;
        flags = child.getSurfaceTouchableRegion(inputWindowHandle, flags);
        inputWindowHandle.layoutParamsFlags = flags;
        inputWindowHandle.layoutParamsType = type;
        inputWindowHandle.dispatchingTimeoutNanos = child.getInputDispatchingTimeoutNanos();
        inputWindowHandle.visible = isVisible;
        inputWindowHandle.canReceiveKeys = child.canReceiveKeys();
        inputWindowHandle.hasFocus = hasFocus;
        inputWindowHandle.hasWallpaper = hasWallpaper;
        inputWindowHandle.paused = child.mActivityRecord != null ? child.mActivityRecord.paused : false;
        inputWindowHandle.ownerPid = child.mSession.mPid;
        inputWindowHandle.ownerUid = child.mSession.mUid;
        inputWindowHandle.inputFeatures = child.mAttrs.inputFeatures;
        inputWindowHandle.displayId = child.getDisplayId();

        final Rect frame = child.getFrameLw();
        inputWindowHandle.frameLeft = frame.left;
        inputWindowHandle.frameTop = frame.top;
        inputWindowHandle.frameRight = frame.right;
        inputWindowHandle.frameBottom = frame.bottom;

        inputWindowHandle.surfaceInset = child.getAttrs().surfaceInsets.left;

        if (child.getTask() != null && child.getTask().isOrganized()) {
            inputWindowHandle.replaceTouchableRegionWithCrop(null /* Use this surfaces crop */);
        }
        if (child.mGlobalScale != 1) {
            inputWindowHandle.scaleFactor = 1.0f/child.mGlobalScale;
        } else {
            inputWindowHandle.scaleFactor = 1;
        }
    }

填充完毕InputWindowHandle后,会将InputWindowHandle设置给mInputTransaction对象:

// frameworks/base/core/java/android/view/SurfaceControl.java

        public Transaction setInputWindowInfo(SurfaceControl sc, InputWindowHandle handle) {
            checkPreconditions(sc);
            nativeSetInputWindowInfo(mNativeObject, sc.mNativeObject, handle);
            return this;
        }

当遍历完成后,回到updateInputWindows()方法的最后,将mInputTransaction对象合并到了`DisplayContent#mPendingTransaction``统一进行事务提交,最终通过SurfaceFlinger设置给了InputDispatcher:

// frameworks/native/services/inputflinger/dispatcher/InputDispatcher.cpp

void InputDispatcher::setInputWindows( const std::unordered_map<int32_t, std::vector<sp<InputWindowHandle>>>& handlesPerDisplay) {
    { // acquire lock
        std::scoped_lock _l(mLock);
        for (auto const& i : handlesPerDisplay) {
            setInputWindowsLocked(i.second, i.first);
        }
    }
    mLooper->wake();
}

在InputDispatcher事件派发过程中,有一个寻找派发目标窗口的步骤,就是从WMS中传递过来的InputWindowHandle中寻找满足条件的InputWIndowHandle。

在Android P上,是直接通过JNI的方式设置的,这点有所不同。

3.总结

以上就是系统焦点窗口和InputWindows的更新流程,相比而言流程比较清晰简单。了解InputWindow,对后续学习Input事件派发过程有非常大的帮忙,这部分将会在Input模块中进行总结。

点击这里复制本文地址 以上内容由权冠洲的博客整理呈现,请务必在转载分享时注明本文地址!如对内容有疑问,请联系我们,谢谢!

支持Ctrl+Enter提交

联系我们| 本站介绍| 留言建议 | 交换友链 | 域名展示
本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除

权冠洲的博客 © All Rights Reserved.  Copyright quanguanzhou.top All Rights Reserved
苏公网安备 32030302000848号   苏ICP备20033101号-1

联系我们