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

2,158 阅读11分钟

前面一篇文章,分析了 InputReader 处理 motion event,经过对事件的加工,生成高级事件 NotifyMotionArgs。InputReader 最终会把事件发送给 InputDispatcher,如下

void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) {
    if (debugInboundEventDetails()) {
        ALOGD("notifyMotion - id=%" PRIx32 " eventTime=%" PRId64 ", deviceId=%d, source=%s, "
              "displayId=%" PRId32 ", policyFlags=0x%x, "
              "action=%s, actionButton=0x%x, flags=0x%x, metaState=0x%x, buttonState=0x%x, "
              "edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, xCursorPosition=%f, "
              "yCursorPosition=%f, downTime=%" PRId64,
              args.id, args.eventTime, args.deviceId, inputEventSourceToString(args.source).c_str(),
              args.displayId, args.policyFlags, MotionEvent::actionToString(args.action).c_str(),
              args.actionButton, args.flags, args.metaState, args.buttonState, args.edgeFlags,
              args.xPrecision, args.yPrecision, args.xCursorPosition, args.yCursorPosition,
              args.downTime);
        for (uint32_t i = 0; i < args.pointerCount; i++) {
            ALOGD("  Pointer %d: id=%d, toolType=%s, x=%f, y=%f, pressure=%f, size=%f, "
                  "touchMajor=%f, touchMinor=%f, toolMajor=%f, toolMinor=%f, orientation=%f",
                  i, args.pointerProperties[i].id,
                  ftl::enum_string(args.pointerProperties[i].toolType).c_str(),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_X),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_Y),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_PRESSURE),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_SIZE),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MAJOR),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOUCH_MINOR),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MAJOR),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_TOOL_MINOR),
                  args.pointerCoords[i].getAxisValue(AMOTION_EVENT_AXIS_ORIENTATION));
        }
    }

    // ...

    uint32_t policyFlags = args.policyFlags;
    policyFlags |= POLICY_FLAG_TRUSTED;

    android::base::Timer t;
    
    // 先把 motion event 发送给策略,看看策略是否截断
    // 如果截断了,在 policyFlags 中去掉 POLICY_FLAG_PASS_TO_USER
    mPolicy.interceptMotionBeforeQueueing(args.displayId, args.eventTime, policyFlags);
    
    // 策略处理的过慢,这里会有警告 log
    if (t.duration() > SLOW_INTERCEPTION_THRESHOLD) {
        ALOGW("Excessive delay in interceptMotionBeforeQueueing; took %s ms",
              std::to_string(t.duration().count()).c_str());
    }

    bool needWake = false;
    { // acquire lock
        mLock.lock();
        
        // ...

        // 创建 MotionEntry,包装 motion event 数据
        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);

        // ...

        // MotionEntry 加入到 inbound queue 
        needWake = enqueueInboundEventLocked(std::move(newEntry));
        mLock.unlock();
    } // release lock

    // 如果有必要,唤醒 InputDispatcher 线程,处理 inbound queue 中的事件
    if (needWake) {
        mLooper->wake();
    }
}

InputDispatcher 收到 motion event 后

  1. 询问策略是否截断 motion event。
  2. motion event 保存到 InputDispatcher inbound queue 中,并唤醒 InputDispatcher 线程处理 inbound queue 中的 motion event。

截断策略

// 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;
    }

    // 来自触摸屏的 motion event,是受信任的,并且非注入的
    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 来说,如果系统处于交互状态,例如,显示屏处于亮屏状态,策略不截断。而如果系统处于非交互状态,例如,显示屏处于灭屏状态,需要询问上层策略是否截断,如下

// PhoneWindowManager.java

// 注意,这是在非交互状态下,决定是否分发 motion event
public int interceptMotionBeforeQueueingNonInteractive(int displayId, long whenNanos,
        int policyFlags) {
    
    // 如果 motion event 唤醒设备,那么不需要分发,即策略截断了
    if ((policyFlags & FLAG_WAKE) != 0) {
        if (wakeUp(whenNanos / 1000000, mAllowTheaterModeWakeFromMotion,
                PowerManager.WAKE_REASON_WAKE_MOTION, "android.policy:MOTION")) {
            // 0 表示截断了 motion event
            return 0;
        }
    }

    // 在设备处于非交互状态下,只有某些特殊情况,才允许分发 motion event
    if (shouldDispatchInputWhenNonInteractive(displayId, KEYCODE_UNKNOWN)) {
        return ACTION_PASS_TO_USER;
    }
    
    
    // theater mode 下,motion event 唤醒了设备,也不分发,即策略截断了
    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);

    // 显示屏处于 OFF 状态,并且非手表设备,不分发 motion event,即截断
    if (displayOff && !mHasFeatureWatch) {
        return false;
    }

    // 显示屏处于非 OFF 状态,并且有锁屏,需要分发 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;
    }

    // dream 模式下,也需要分发 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;
}    

对于 motion event 来说,当设备处于非交互状态

  1. 如果是手机/平板设备,并且显示屏处于 OFF 状态,motion event 不会分发,即策略截断了事件。
  2. 显示屏处于非 OFF 状态,例如, DOZE 状态,并且还有锁屏,那么 motion event 需要分发给锁屏,因此策略不截断事件。
  3. 如果系统处于 dream 模式下,例如,显示 AOD 组件,那么 motion event 需要分发给 AOD 组件,即策略不截断。

分发 motion event

motion event 与 key event 一样,InputDispatcher 都是通过线程循环进行分发。这里省略线程循环的代码,直接看分发 motion event 的方法,如下

bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
                                           DropReason* dropReason, nsecs_t* nextWakeupTime) {
    ATRACE_CALL();
    
    if (!entry->dispatchInProgress) {
        entry->dispatchInProgress = true;
        
        // 这里会打印 motion event 数据
        logOutboundMotionDetails("dispatchMotion - ", *entry);
    }

    // Clean up if dropping the event.
    if (*dropReason != DropReason::NOT_DROPPED) {
        setInjectionResult(*entry,
                           *dropReason == DropReason::POLICY ? InputEventInjectionResult::SUCCEEDED
                                                             : InputEventInjectionResult::FAILED);
        return true;
    }

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

    // inputTargets 保存的是要发送 motion event 的所有目标窗口
    // Identify targets.
    std::vector<InputTarget> inputTargets;

    bool conflictingPointerActions = false;
    InputEventInjectionResult injectionResult;
    if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)

        if (mDragState &&
            (entry->action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN) {
            // ...
        }
        
        // 1. 获取触摸的窗口
        inputTargets =
                findTouchedWindowTargetsLocked(currentTime, *entry, &conflictingPointerActions,
                                               /*byref*/ injectionResult);
        LOG_ALWAYS_FATAL_IF(injectionResult != InputEventInjectionResult::SUCCEEDED &&
                            !inputTargets.empty());
    } else {
        // Non touch event.  (eg. trackball)
        // ...
    }

    if (injectionResult == InputEventInjectionResult::PENDING) {
        return false;
    }

    setInjectionResult(*entry, injectionResult);
    
    if (injectionResult == InputEventInjectionResult::TARGET_MISMATCH) {
        return true;
    }
    
    if (injectionResult != InputEventInjectionResult::SUCCEEDED) {
        CancelationOptions::Mode mode(
                isPointerEvent ? CancelationOptions::Mode::CANCEL_POINTER_EVENTS
                               : CancelationOptions::Mode::CANCEL_NON_POINTER_EVENTS);
        CancelationOptions options(mode, "input event injection failed");
        synthesizeCancelationEventsForMonitorsLocked(options);
        return true;
    }

    // global monitor 窗口,也是 motion event 的目标窗口,这里不讨论
    // Add monitor channels from event's or focused display.
    addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));

    // Dispatch the motion.
    if (conflictingPointerActions) {
        CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS,
                                   "conflicting pointer actions");
        synthesizeCancelationEventsForAllConnectionsLocked(options);
    }
    
    // 2. 分发 motion event 给所有目标窗口
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

寻找触摸的窗口

key event 是发送给焦点窗口,而对于单手指的 motion event, 无论是 down event,还是 move event 和 up event,都是发送给 down event 的 x,y 坐标落入的窗口。

因此,直接来分析如何为 motion down event 寻找触摸的窗口,而对于 motion move event 和 motion up event 的触摸窗口,留给读者自行分析。

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

    // targets 保存 motion event 所有的目标窗口
    std::vector<InputTarget> targets;
    
    const int32_t displayId = entry.displayId;
    const int32_t action = entry.action;
    // 单手指的 down event 的 action 为 AMOTION_EVENT_ACTION_DOWN
    const int32_t maskedAction = MotionEvent::getActionMasked(action);

    outInjectionResult = InputEventInjectionResult::PENDING;

    const TouchState* oldState = nullptr;
    TouchState tempTouchState;
    // 对 down event 来说,此时 mTouchStatesByDisplay 没有数据
    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) {
        // If a new gesture is starting, clear the touch state completely.
        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)) {
    
        // 解析 motion down event 的 x,y 坐标
        const auto [x, y] = resolveTouchedPosition(entry);
        
        // 从 action 中获取 pointer index
        const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
        
        // false
        const bool isStylus = isPointerFromStylus(entry, pointerIndex);
        
        // false
        const bool isFromCrossDevice = entry.flags & AMOTION_EVENT_FLAG_FROM_DEVICE_INTEGRATION_SERVICE;
        
        // 1. 寻找触摸的窗口
        // 返回的数据中,newTouchedWindowHandle 是 x,y 坐标落入的窗口(排除 spy window)
        // outsideTargets 是所有监听 outside touch event 的窗口
        auto [newTouchedWindowHandle, outsideTargets] =
                findTouchedWindowAtLocked(displayId, x, y, isStylus, false /*ignoreDragWindow*/, isFromCrossDevice);
                
        // targets 保存监听 outside touch event 的窗口
        // 因为这种窗口的特性是,当 motion down event 落入窗口之外时,会收到 outside event
        if (isDown) {
            targets += outsideTargets;
        }

        // Handle the case where we did not find a window.
        if (newTouchedWindowHandle == nullptr) {
            // ...
        }

        // Verify targeted injection.
        if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
            // ...
        }

        // ...

        // 获取 x,y 坐标落入的所有 spy windows
        // 注意,这些 spy windows 必须在 newTouchedWindowHandle 之上
        // spy window 的特性是,它会接收 motion event,同时也会把 motion event 传递给其下的窗口
        std::vector<sp<WindowInfoHandle>> newTouchedWindows =
                findTouchedSpyWindowsAtLocked(displayId, x, y, isStylus);
        if (newTouchedWindowHandle != nullptr) {
            // newTouchedWindowHandle 加入到 spy windows 集合的队首位置,因为 motion event 要首先分发给它
            // newTouchedWindows 现在保存是 motion event 要分发给的所有目标窗口
            newTouchedWindows.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
        }

        if (newTouchedWindows.empty()) {
            // ...
            return {};
        }

        // 2. 遍历所有的目标窗口,并用 tempTouchState.windows 收集有效的目标窗口
        for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
            // 检测窗口是否能接收 motion event
            // 例如,没有 input channel,不能接收 motion event
            if (!canWindowReceiveMotionLocked(windowHandle, entry)) {
                continue;
            }

            // ...

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

            // Determines if the given window can be targeted as InputTarget::Flags::FOREGROUND.
            // 非 spy window,并且可以聚焦,就可以称之为接收 motion event 的前台窗口
            // 例如,Activity 真窗,就是一个前台窗口
            if (canReceiveForegroundTouches(*windowHandle->getInfo())) {
                // There should only be one touched window that can be "foreground" for the pointer.
                targetFlags |= InputTarget::Flags::FOREGROUND;
            }

            // ...
            
            // 检测遍历的目标窗口在 x,y 坐标处是否被遮挡,还是被完全遮挡
            if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
                targetFlags |= InputTarget::Flags::WINDOW_IS_OBSCURED;
            } else if (isWindowObscuredLocked(windowHandle)) {
                targetFlags |= InputTarget::Flags::WINDOW_IS_PARTIALLY_OBSCURED;
            }

            // Update the temporary touch state.
            // pointerIds 保存遍历的目标窗口的 pointer 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;

            // tempTouchState 保存目标窗口
            // 其实就是创建 TouchedWindow,保存到  tempTouchState.windows 列表中
            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds,
                                             isDownOrPointerDown
                                                     ? std::make_optional(entry.eventTime)
                                                     : std::nullopt);

            // If this is the pointer going down and the touched window has a wallpaper
            // then also add the touched wallpaper windows so they are locked in for the duration
            // of the touch gesture.
            // We do not collect wallpapers during HOVER_MOVE or SCROLL because the wallpaper
            // engine only supports touch events.  We would need to add a mechanism similar
            // to View.onGenericMotionEvent to enable wallpapers to handle these events.
            if (isDownOrPointerDown) {
                // 如果前台窗口需要显示壁纸,那么 tempTouchState 保存壁纸窗口,因为它也可以作为 motion event 的目标窗口
                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);
                    }
                }
            }
        }

        // ...
    } else {
        // ...
    }


    // ...

    // 3. tempTouchState.windows 保存的是所有有效的目标窗口,现在为每一个窗口创建 InputTarget
    // 然后保存到 targets
    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
        if (touchedWindow.pointerIds.none() && !touchedWindow.hasHoveringPointers(entry.deviceId)) {
            continue;
        }

        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                              touchedWindow.pointerIds, touchedWindow.firstDownTimeInTarget,
                              targets);
    }

    // ...

    outInjectionResult = InputEventInjectionResult::SUCCEEDED;

    // tempTouchState.windows 只保存 target flags 为 DISPATCH_AS_IS 的窗口
    // 也就是说,移除 outside touch windows
    // Drop the outside or hover touch windows since we will not care about them
    // in the next iteration.
    tempTouchState.filterNonAsIsTouchWindows();

    // ...

    // 4. 返回所有的目标
    return targets;
}

为了更直观的分析,我将省略一些功能的代码分析,例如,split touch,outside touche event,spy window 等等,读者看下注释,了解下即可。

第一步,根据 motion down event 的 x,y 坐标,获取触摸的窗口,如下

std::pair<sp<WindowInfoHandle>, std::vector<InputTarget>>
InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, float x, float y, bool isStylus,
#ifdef DISABLE_DEVICE_INTEGRATION
                                           bool ignoreDragWindow) const {
#else
                                           bool ignoreDragWindow,
                                           bool isFromCrossDevice) const {
#endif
    // 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) {
        if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
            continue;
        }
#ifndef DISABLE_DEVICE_INTEGRATION
        const WindowInfo* windowInfo = windowHandle->getInfo();
        // Device Integration: bypass the event in black screen if it coming from DIS
        bool bypassBlackScreen = (windowInfo->layoutParamsType == WindowInfo::Type::SYSTEM_BLACKSCREEN_OVERLAY)
                                            && isFromCrossDevice;
        if (bypassBlackScreen) {
            continue;
        }
#endif

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

        // 排除 spy window,找到 x,y 坐标落入的窗口
        if (!info.isSpy() &&
            windowAcceptsTouchAt(info, displayId, x, y, isStylus, getTransformLocked(displayId))) {
            // 从这里可以看出,outsideTargets 保存的窗口,是位于 windowHandle 之上
            return {windowHandle, outsideTargets};
        }

        // 监听 outside touch event 的窗口,保存到 outsideTargets
        // 例如 Dialog 就可以监听 outside touch event
        if (info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
            addWindowTargetLocked(windowHandle, InputTarget::Flags::DISPATCH_AS_OUTSIDE,
                                  /*pointerIds=*/{}, /*firstDownTimeInTarget=*/std::nullopt,
                                  outsideTargets);
        }
    }
    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;
    
    // 同一个 display 下,并且窗口必须可见
    if (windowInfo.displayId != displayId ||
        inputConfig.test(WindowInfo::InputConfig::NOT_VISIBLE)) {
        return false;
    }
    
    // 触控笔相关
    // false
    const bool windowCanInterceptTouch = isStylus && windowInfo.interceptsStylus();
    // 窗口必须可触摸
    // 例如,启动窗口不可触摸,所以启动窗口的 root view 不会收到 touch event
    if (inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) && !windowCanInterceptTouch) {
        return false;
    }

    // 检测 x,y 坐标是否在窗口可触摸区域
    // displayTransform 的作用是把物理屏坐标转换为逻辑屏坐标
    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;
}

第二步,遍历可以作为 motion down event 目标的窗口

  1. 过滤掉无法接收 motion event 的窗口。例如,启动窗口没有 input channel,它无法接收 motion event。
  2. 解析目标窗口的 flags,其中最重要的是解析前台窗口 flag,例如,Activity 真窗就是一个前台窗口。
  3. 把有效的目标窗口,保存到tempTouchState.windows 集合中。
    // TouchState.cpp
    
    void TouchState::addOrUpdateWindow(const sp<WindowInfoHandle>& windowHandle,
                                       ftl::Flags<InputTarget::Flags> targetFlags,
                                       std::bitset<MAX_POINTER_ID + 1> pointerIds,
                                       std::optional<nsecs_t> firstDownTimeInTarget) {
        for (TouchedWindow& touchedWindow : windows) {
            // ...
        }
        TouchedWindow touchedWindow;
        touchedWindow.windowHandle = windowHandle;
        touchedWindow.targetFlags = targetFlags;
        touchedWindow.pointerIds = pointerIds;
        touchedWindow.firstDownTimeInTarget = firstDownTimeInTarget;
        windows.push_back(touchedWindow);
    }
    
  4. 如果前台窗口显示了壁纸,那么 tempTouchState.windows 还会保存壁纸窗口。例如,Launcher 就需要显示壁纸,如果在 Launcher 窗口上触摸,壁纸窗口也可以作为 motion event 的目标窗口。

第三步,为 tempTouchState.windows 保存的每一个有效目标窗口,创建 InputTarget,并保存到 targets 中。

void InputDispatcher::addWindowTargetLocked(const sp<WindowInfoHandle>& windowHandle,
                                            ftl::Flags<InputTarget::Flags> targetFlags,
                                            std::bitset<MAX_POINTER_ID + 1> pointerIds,
                                            std::optional<nsecs_t> firstDownTimeInTarget,
                                            std::vector<InputTarget>& inputTargets) const {
    std::vector<InputTarget>::iterator it =
            std::find_if(inputTargets.begin(), inputTargets.end(),
                         [&windowHandle](const InputTarget& inputTarget) {
                             return inputTarget.inputChannel->getConnectionToken() ==
                                     windowHandle->getToken();
                         });

    const WindowInfo* windowInfo = windowHandle->getInfo();

    if (it == inputTargets.end()) {
        // 创建 InputTarget
        std::optional<InputTarget> target =
                createInputTargetLocked(windowHandle, targetFlags, firstDownTimeInTarget);
        if (!target) {
            return;
        }
        
        // 保存到 inputTargets
        inputTargets.push_back(*target);
        it = inputTargets.end() - 1;
    }

    ALOG_ASSERT(it->flags == targetFlags);
    ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);

    // InputTarget::pointerTransforms 保存 pointer id 对应的 window transform
    // TODO: windowInfo->transform 应该是把 logical display 坐标转化为窗口坐标
    it->addPointers(pointerIds, windowInfo->transform);
}

第四步,至此 targets 保存了 motion down event 所有的目标窗口,返回给 InputDispatcher。

分发 motion event 给目标窗口

分发事件的流程都是一样的,无论是分发 key event,还是分发 motion event,都是为每一个目标窗口开启一个分发循环,把事件发送给窗口,然后等待窗口反馈处理结果。详细流程,请参考Andorid U Input系统:InputDispatcher 分发按键事件