Android U Input 系统:InputDispatcher 分发触摸事件

1,544 阅读9分钟

根据前面文章的分析,InputReader 处理 motion event 时,会对 raw event 进行加工,然后生成高级事件,例如 ACTION_DOWN, ACTION_MOVE, ACTION_UP。本文来分析 InputDispatcher 如何把这些高级事件,分发给窗口。

void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) {
    // ...

    uint32_t policyFlags = args.policyFlags;
    // 来自输入设备的 motion event,都是受信任的
    policyFlags |= POLICY_FLAG_TRUSTED;

    // 1. 询问分发策略
    mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);


    bool needWake = false;
    { // acquire lock
        mLock.lock();
        if (!(policyFlags & POLICY_FLAG_PASS_TO_USER)) {
            
        }

        // 不讨论 input filter 功能
        if (shouldSendMotionToInputFilterLocked(args)) {
            
        }

        // 2. 加入到 inbound queue
        std::unique_ptr<MotionEntry> newEntry =
                std::make_unique<MotionEntry>(args.id, args.eventTime, args.deviceId, args.source,
                                              args.displayId, policyFlags, args.action,
                                              args.actionButton, args.flags, args.metaState,
                                              args.buttonState, args.classification, args.edgeFlags,
                                              args.xPrecision, args.yPrecision,
                                              args.xCursorPosition, args.yCursorPosition,
                                              args.downTime, args.pointerCount,
                                              args.pointerProperties, args.pointerCoords);
        needWake = enqueueInboundEventLocked(std::move(newEntry));
        mLock.unlock();
    } // release lock

    // 3. 如果有必要,唤醒线程处理事件
    if (needWake) {
        mLooper->wake();
    }
}

InputDispatcher 收到高级的 motion event 后,仍然是先让分发策略处理,然后加入到 inbound queue 中,最后唤醒线程来处理。

分发策略

// com_android_server_input_InputManagerService.cpp

void NativeInputManager::interceptMotionBeforeQueueing(int32_t displayId, nsecs_t when,
                                                       uint32_t& policyFlags) {
    ATRACE_CALL();

    const bool interactive = mInteractive.load();
    if (interactive) {
        policyFlags |= POLICY_FLAG_INTERACTIVE;
    }

    if ((policyFlags & POLICY_FLAG_TRUSTED) == 0 || (policyFlags & POLICY_FLAG_INJECTED)) {
        
    }

    // 交互状态下,motion event 不需要经过上层的分发策略处理,直接就允许分发
    if (policyFlags & POLICY_FLAG_INTERACTIVE) {
        policyFlags |= POLICY_FLAG_PASS_TO_USER;
        return;
    }

    JNIEnv* env = jniEnv();
    // 非交互状态下,motion event 是否要分发,还得询问上层分发策略
    const jint wmActions =
            env->CallIntMethod(mServiceObj,
                               gServiceClassInfo.interceptMotionBeforeQueueingNonInteractive,
                               displayId, when, policyFlags);
    if (checkAndClearExceptionFromCallback(env, "interceptMotionBeforeQueueingNonInteractive")) {
        return;
    }
    handleInterceptActions(wmActions, when, /*byref*/ policyFlags);
}

void NativeInputManager::handleInterceptActions(jint wmActions, nsecs_t when,
        uint32_t& policyFlags) {
    if (wmActions & WM_ACTION_PASS_TO_USER) {
        // 上层分发策略允许分发,policyFlags 中添加 POLICY_FLAG_PASS_TO_USER
        policyFlags |= POLICY_FLAG_PASS_TO_USER;
    } else {
#if DEBUG_INPUT_DISPATCHER_POLICY
        ALOGD("handleInterceptActions: Not passing key to user.");
#endif
    }
}

在交互状态下,例如亮屏状态,motion event 是不需要经过上层分发策略处理,就直接允许分发。

而如果在非交互状态下,例如灭屏,motion event 由上层的分发策略决定是否分发,如下

// PhoneWindowManager.java

    // 注意,这是在非交互状态下,决定是否分发 motion event
    public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
            int policyFlags) {
        if ((policyFlags & FLAG_WAKE) != 0) {
            if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
                    PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
                // 如果唤醒屏幕,不分发 motion event
                return 0;
            }
        }

        // 在设备处于非交互状态下,只有某些特殊情况,才允许分发 motion event
        if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) {
            return ACTION_PASS_TO_USER;
        }

        // If we have not passed the action up and we are in theater mode without dreaming,
        // there will be no dream to intercept the touch and wake into ambient.  The device should
        // wake up in this case.
        if (isTheaterModeEnabled() && (policyFlags & FLAG_WAKE) != 0) {
            wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotionWhenNotDreaming,
                    PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION");
        }

        // 默认不允许分发 motion event
        return 0;
    }

    private boolean shouldDispatchInputWhenNonInteractive(int displayId, int keyCode) {
        // Apply the default display policy to unknown displays as well.
        final boolean isDefaultDisplay = displayId == DEFAULT_DISPLAY
                || displayId == INVALID_DISPLAY;
        final Display display = isDefaultDisplay
                ? mDefaultDisplay
                : mDisplayManager.getDisplay(displayId);
        final boolean displayOff = (display == null
                || display.getState() == STATE_OFF);

        // 灭屏,非手表设备,不允许分发 motion event
        if (displayOff && !mHasFeatureWatch) {
            return false;
        }

        // 走到这里,表示屏幕处于非灭屏,或者是手表设备


        // 设备处于非灭屏,并且有锁屏显示,需要分发 motion event 给锁屏
        // Send events to keyguard while the screen is on and it's showing.
        if (isKeyguardShowingAndNotOccluded() && !displayOff) {
            return true;
        }

        // Watches handle BACK and hardware buttons specially
        if (mHasFeatureWatch && (keyCode == KeyEvent.KEYCODE_BACK
                || keyCode == KeyEvent.KEYCODE_STEM_PRIMARY
                || keyCode == KeyEvent.KEYCODE_STEM_1
                || keyCode == KeyEvent.KEYCODE_STEM_2
                || keyCode == KeyEvent.KEYCODE_STEM_3)) {
            return false;
        }

        // 这里应该指的是 AOD 模式下,是允许分发 motion event 给 AOD 窗口的
        if (isDefaultDisplay) {
            IDreamManager dreamManager = getDreamManager();

            try {
                if (dreamManager != null && dreamManager.isDreaming()) {
                    return true;
                }
            } catch (RemoteException e) {
                Slog.e(TAG, "RemoteException when checking if dreaming", e);
            }
        }

        // 默认,不允许分发 motion event
        return false;
    }    

对于手机来说,设备在处于非交互状态下,并且屏幕处于 OFF 状态下,是不允许分发 motion event 的。

但是,如果屏幕处于非 OFF 状态,例如 doze 状态,如果此时有锁屏窗口 或者 AOD 窗口,那么是允许分发 motion event 的,因为这些窗口,需要处理 motion event。

分发

void InputDispatcher::dispatchOnce() {
    nsecs_t nextWakeupTime = LLONG_MAX;
    { // acquire lock
        std::scoped_lock _l(mLock);
        mDispatcherIsAlive.notify_all();

        if (!haveCommandsLocked()) {
            // 通过分发循环分发 motion event
            dispatchOnceInnerLocked(&nextWakeupTime);
        }

        // ...
    } // release lock

    // ...
}

void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
    // ...

    if (!mPendingEvent) {
        if (mInboundQueue.empty()) {

        } else {
            // 从 inbound queue 中取出事件
            mPendingEvent = mInboundQueue.front();
            mInboundQueue.pop_front();
            traceInboundQueueLengthLocked();
        }

        // ...
    }

    // ...

    switch (mPendingEvent->type) {
        // ...

        case EventEntry::Type::MOTION: {
            std::shared_ptr<MotionEntry> motionEntry =
                    std::static_pointer_cast<MotionEntry>(mPendingEvent);
            // ...

            // 分发 motion event
            done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
            break;
        }

        // ...
    }

    // 成功分发事件
    if (done) {
        if (dropReason != DropReason::NOT_DROPPED) {
            dropInboundEventLocked(*mPendingEvent, dropReason);
        }
        mLastDropReason = dropReason;

        // 重置 mPendingEvent 等等
        releasePendingEventLocked();
        *nextWakeupTime = LLONG_MIN; // force next poll to wake up immediately
    }
}


bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
                                           DropReason* dropReason, nsecs_t* nextWakeupTime) {
    // ...


    // 由丢弃的理由,不分发
    // 例如,分发策略不允许分发
    if (*dropReason != DropReason::NOT_DROPPED) {
        setInjectionResult(*entry,
                           *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
                                                             : InputEventInjectionResult::FAILED);
        return true;
    }

    const bool isPointerEvent = isFromSource(entry->source, AINPUT_SOURCE_CLASS_POINTER);

    // Identify targets.
    std::vector<InputTarget> inputTargets;

    bool conflictingPointerActions = false;
    InputEventInjectionResult injectionResult;
    if (isPointerEvent) {
        // ...

        // 找到手指触摸的窗口
        inputTargets =
                findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions,
                                               /*byref*/ injectionResult);
    } else {
        
    }
    if (injectionResult == InputEventInjectionResult::PENDING) {
        return false;
    }

    setInjectionResult(*entry, injectionResult);
   
    // ...

    // 本文不讨论 monitor channel 处理 motion event
    // 例如,返回手势,就是根据这个实现的
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));

    // ...

    
    // 通过分发循环,把事件发送给窗口
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

原理很简单,先为 motion event 寻找触摸的窗口,然后通过分发循环,把 motion event 发送给窗口。

分发循环,在前面分析按键事件分发的时候,已经分析过,这里不重复分析。下面重点分析如何为 motion event 找到触摸的窗口。

寻找触摸的窗口

根据前面的分析,InputReader 处理 raw motion event ,会生成 MOTION_DOWN, MOTION_MOVE, MOTION_UP 这样的高级事件。而为这些高级事件,寻找触摸的窗口的原理却不同,因此需要分开来分析

另外,为了简化分析,会省略某些高级功能的代码,例如 split touch。而剩下的代码,就是最基础的原理。一旦掌握了最基本的原理,就可以分析更高级的功能。

为 ACTION_DOWN 寻找触摸的窗口

std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
        nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
        InputEventInjectionResult& outInjectionResult) {
    ATRACE_CALL();

    std::vector<InputTarget> targets;
    const int32_t displayId = entry.displayId;
    const int32_t action = entry.action;
    const int32_t maskedAction = MotionEvent::getActionMasked(action);

    outInjectionResult = InputEventInjectionResult::PENDING;

    const TouchState* oldState = nullptr;
    TouchState tempTouchState;
    if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
        
    }

    // ...

    // false
    const bool wasDown = oldState != nullptr && oldState->isDown();
    // true
    const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
            (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
    // true
    const bool newGesture = isDown || maskedAction == AMOTION_EVENT_ACTION_SCROLL ||
            maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
            maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE;

    // ...

    if (newGesture) {
        tempTouchState.reset();
        tempTouchState.deviceId = entry.deviceId;
        tempTouchState.source = entry.source;
        isSplit = false;
    } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {

    }

    // ...

    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
        
        // 1. 获取 motion event 保存的 x,y 坐标
        const auto [x, y] = resolveTouchedPosition(entry);

        const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
        const bool isStylus = isPointerFromStylus(entry, pointerIndex);

        // 2.获取触摸的窗口
        auto [newTouchedWindowHandle, outsideTargets] =
                findTouchedWindowAtLocked(displayId, x, y, isStylus);

        // ...


        // 3.获取所有触摸的 spy window,它们也可以作为 motion event 分发的窗口
        std::vector<sp<WindowInfoHandle>> newTouchedWindows =
                findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
        if (newTouchedWindowHandle != nullptr) {
            // Process the foreground window first so that it is the first to receive the event.
            // newTouchedWindows 保存了所有触摸的窗口,包括 spy window
            newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
        }

        // ...

        // 4.遍历所有触摸的窗口
        for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {

            // 过滤不能处理 motion event 的窗口,例如,窗口没有 input channel
            if (!canWindowReceiveMotionLocked(windowHandle, entry)) {
                continue;
            }

            // ...

            // Set target flags.
            ftl::Flags<InputTarget::Flags> targetFlags = InputTarget::Flags::DISPATCH_AS_IS;

            // 如果触摸的窗口是前台窗口,添加标志位
            // 可触摸的,并且非 spy window,是前台窗口
            if (canReceiveForegroundTouches(*windowHandle->getInfo())) {
                // There should only be one touched window that can be "foreground" for the pointer.
                targetFlags |= InputTarget::Flags::FOREGROUND;
            }

            // ..

            // 保存手指 id
            std::bitset<MAX_POINTER_ID + 1> pointerIds;
            if (!isHoverAction) {
                pointerIds.set(entry.pointerProperties[pointerIndex].id);
            }

            // true
            const bool isDownOrPointerDown = maskedAction == AMOTION_EVENT_ACTION_DOWN ||
                    maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN;


            // 4.1 保存触摸的窗口到 tempTouchState.windows 集合中
            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds,
                                             isDownOrPointerDown
                                                     ? std::make_optional(entry.eventTime)
                                                     : std::nullopt);

     
            // 如果这个 foreground 窗口,还会显示壁纸,例如 launcher,那么也把壁纸窗口作为输入窗口
            if (isDownOrPointerDown) {
                if (targetFlags.test(InputTarget::Flags::FOREGROUND) &&
                    windowHandle->getInfo()->inputConfig.test(
                            gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
                    sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle);
                    if (wallpaper != nullptr) {
                        ftl::Flags<InputTarget::Flags> wallpaperFlags =
                                InputTarget::Flags::WINDOW_IS_OBSCURED |
                                InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED |
                                InputTarget::Flags::DISPATCH_AS_IS;
                        if (isSplit) {
                            wallpaperFlags |= InputTarget::Flags::SPLIT;
                        }
                        tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, pointerIds,
                                                         entry.eventTime);
                    }
                }
            }
        } // 遍历所有 touched window 结束

        // ...

    } else {
        
    }

    // ...

    // Output targets from the touch state.
    // 现在,tempTouchState.windows 保存的是有效的触摸的窗口
    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
        if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
            continue;
        }

        // 5.把 empTouchState.windows 保存的窗口,保存到 targets
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                              touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
                              targets);
    }

    // ...

    outInjectionResult = InputEventInjectionResult::SUCCEEDED;

    // ...

    if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
        if (displayId >= 0) {
            tempTouchState.clearWindowsWithoutPointers();
            // tempTouchState 保存到 mTouchStatesByDisplay
            mTouchStatesByDisplay[displayId] = tempTouchState;
        } else {
            
        }
    }

    // ...

    // 6. 返回找到的触摸的窗口
    return targets;
}

为 action down 寻找触摸窗口,其实是根据 x,y 坐标寻找触摸的窗口,如下

std::pair<sp<WindowInfoHandle>, std::vector<InputTarget>>
InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus,
                                           bool ignoreDragWindow) const {

    // Traverse windows from front to back to find touched window.
    std::vector<InputTarget> outsideTargets;
    const auto& windowHandles = getWindowHandlesLocked(displayId);
    for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
        // ...

        const WindowInfo& info = *windowHandle->getInfo();

        // 获取x,y坐标落入的 非spy 窗口
        if (!info.isSpy() &&
            windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
            return {windowHandle, outsideTargets};
        }

        // 本文不讨论 watch outside 窗口的情况
        if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
            
        }
    }
    return {nullptr, {}};
}

bool windowAcceptsTouchAt(const WindowInfo& windowInfo, int32_t displayId, float x, float y,
                          bool isStylus, const ui::Transform& displayTransform) {
    const auto inputConfig = windowInfo.inputConfig;

    // 窗口必须要可见,才能收到 motion event
    if (windowInfo.displayId != displayId ||
        inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
        return false;
    }

    // 触控笔相关
    // false
    const bool windowCanInterceptTouch = isStylus && windowInfo.interceptsStylus();

    // 窗口必须可触摸
    if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
        return false;
    }

    // 判断 x,y 是否落入某个窗口可触摸区域
    // Window Manager works in the logical display coordinate space. When it specifies bounds for a
    // window as (l, t, r, b), the range of x in [l, r) and y in [t, b) are considered to be inside
    // the window. Points on the right and bottom edges should not be inside the window, so we need
    // to be careful about performing a hit test when the display is rotated, since the "right" and
    // "bottom" of the window will be different in the display (un-rotated) space compared to in the
    // logical display in which WM determined the bounds. Perform the hit test in the logical
    // display space to ensure these edges are considered correctly in all orientations.
    const auto touchableRegion = displayTransform.transform(windowInfo.touchableRegion);
    const auto p = displayTransform.transform(x, y);
    if (!touchableRegion.contains(std::floor(p.x), std::floor(p.y))) {
        return false;
    }
    return true;
}

找到触摸的窗口后,最终会保存到 tempTouchState ,tempTouchState 最终会保存到 mTouchStatesByDisplay 中。

为 ACTION MOVE 寻找触摸的窗口

std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
        nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
        InputEventInjectionResult& outInjectionResult) {
    // ...

    const TouchState* oldState = nullptr;
    TouchState tempTouchState;
    if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
        oldState = &(it->second);
        // tempTouchState 保存了 action down 的触摸的窗口
        tempTouchState = *oldState;
    }

    // ...

    // true
    const bool wasDown = oldState != nullptr && oldState->isDown();
    // false
    const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
            (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);

    // ...

    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
        
    } else {
        // ...

        // 处理滑出窗口的情况
        if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
            tempTouchState.isSlippery()) {
            // ...
        }

        // 本文不讨论多手指的 split touch
        if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
           
        }
    }

    // ...


    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
        if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
            continue;
        }
        
        // 把 tempTouchState.windows 中保存的 touched window 保存到 targets
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                              touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
                              targets);
    }

    // ...

    // 返回找到的触摸窗口
    return targets;
}

省略一些特殊的情况,其实 ACTION MOVE 的触摸窗口,仍然是 ACTION DOWN 的触摸窗口。

为 ACTION UP 寻找触摸窗口

std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
        nsecs_t currentTime, const MotionEntry& entry, bool* outConflictingPointerActions,
        InputEventInjectionResult& outInjectionResult) {
    // ...

    const TouchState* oldState = nullptr;
    TouchState tempTouchState;
    if (const auto it = mTouchStatesByDisplay.find(displayId); it != mTouchStatesByDisplay.end()) {
        oldState = &(it->second);
        // tempTouchState 保存的仍然是 ACTION DOWN 的 touched window
        tempTouchState = *oldState;
    }

    // ...

    // true
    const bool wasDown = oldState != nullptr && oldState->isDown();
    // false
    const bool isDown = (maskedAction == AMOTION_EVENT_ACTION_DOWN) ||
            (maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN && !wasDown);
   
    // ...


    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
        
    } else {
        // ...
        
        // 处理滑出窗口的情况
        if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
            tempTouchState.isSlippery()) {
            // ...
        }

        // 本文不讨论多手指 split touch
        if (!isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) {
            
        }
    }

    // ...

    // Output targets from the touch state.
    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
        if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
            continue;
        }

        // 把 tempTouchState.windows 保存的 touched window 保存到 targets
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                              touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
                              targets);
    }


    // ...

    if (isHoverAction) {
        
    } else if (maskedAction == AMOTION_EVENT_ACTION_UP) {
        // Pointer went up.
        // 移除手指id对应的 touched window
        tempTouchState.removeTouchedPointer(entry.pointerProperties[0].id);
    } else if (maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
        
    } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
       
    } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
       
    }

    // ...

    // tempTouchState 此时已经没有 touched window,因此移除 tempTouchState 数据
    if (tempTouchState.windows.empty()) {
        mTouchStatesByDisplay.erase(displayId);
    }

    // 返回寻找到的触摸窗口
    return targets;
}

过滤一些特殊情况,ACTION UP 的触摸窗口,仍然是 ACTION DOWN 的触摸窗口。

TODO

本文的分析,看起来挺简单的,那是因为我省略了很多细节,主要是为了让我们从整体掌握 motion event 的分发流程。

但是,仍然有很多功能,并没有分析,因为涉及窗口的知识,

  1. monitor channel 原理,例如,返回手势
  2. watch outside window
  3. spy window
  4. ANR

如果业余空闲时间足够,我会把这些功能逐个分析。