[Android 13]Input系列--获取触摸窗口

[Android 13]Input系列–获取触摸窗口

hongxi.zhu 2023-7-25
Android 13

InputDispatcher::dispatchMotionLocked

bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, std::shared_ptr<MotionEntry> entry,
                                           DropReason* dropReason, nsecs_t* nextWakeupTime) {
	...
    if (isPointerEvent) {
        // Pointer event.  (eg. touchscreen)
        // 获取触摸目标窗口
        injectionResult =
                findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
                                               &conflictingPointerActions);
    } else {
        // Non touch event.  (eg. trackball)
		...
    }
    ...
    dispatchEventLocked(currentTime, entry, inputTargets);
    return true;
}

InputDispatcher::findTouchedWindowTargetsLocked

InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked(
        nsecs_t currentTime, const MotionEntry& entry, std::vector<InputTarget>& inputTargets,
        nsecs_t* nextWakeupTime, bool* outConflictingPointerActions) {
	
	...
	
    if (newGesture) {  //如果是down事件(说明是一个新的触摸行为)
        bool down = maskedAction == AMOTION_EVENT_ACTION_DOWN;
		
        tempTouchState.reset();
        tempTouchState.down = down;
        tempTouchState.deviceId = entry.deviceId;
        tempTouchState.source = entry.source;
        tempTouchState.displayId = displayId;
        isSplit = false;
    } else if (switchedDevice && maskedAction == AMOTION_EVENT_ACTION_MOVE) {
		...
    }

    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
    	// 如果是down事件
        /* Case 1: New splittable pointer going down, or need target for hover or scroll. */

        int32_t x;
        int32_t y;
        const int32_t pointerIndex = getMotionEventActionPointerIndex(action);
        // Always dispatch mouse events to cursor position.
        if (isFromMouse) {
            x = int32_t(entry.xCursorPosition);
            y = int32_t(entry.yCursorPosition);
        } else {
            x = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_X));
            y = int32_t(entry.pointerCoords[pointerIndex].getAxisValue(AMOTION_EVENT_AXIS_Y));
        }
        const bool isDown = maskedAction == AMOTION_EVENT_ACTION_DOWN;
        const bool isStylus = isPointerFromStylus(entry, pointerIndex);
        newTouchedWindowHandle = findTouchedWindowAtLocked(displayId, x, y, &tempTouchState,
                                                           isStylus, isDown /*addOutsideTargets*/);

        // Handle the case where we did not find a window.
        if (newTouchedWindowHandle == nullptr) {
            ALOGD("No new touched window at (%" PRId32 ", %" PRId32 ") in display %" PRId32, x, y,
                  displayId);
            // Try to assign the pointer to the first foreground window we find, if there is one.
            newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle();
        }

        // Verify targeted injection.
        if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
            ALOGW("Dropping injected touch event: %s", (*err).c_str());
            injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
            newTouchedWindowHandle = nullptr;
            goto Failed;
        }

        // Figure out whether splitting will be allowed for this window.
        if (newTouchedWindowHandle != nullptr) {
            if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
                // New window supports splitting, but we should never split mouse events.
                isSplit = !isFromMouse;
            } else if (isSplit) {
                // New window does not support splitting but we have already split events.
                // Ignore the new window.
                newTouchedWindowHandle = nullptr;
            }
        } else {
            // No window is touched, so set split to true. This will allow the next pointer down to
            // be delivered to a new window which supports split touch. Pointers from a mouse device
            // should never be split.
            tempTouchState.split = isSplit = !isFromMouse;
        }

        // Update hover state.
        if (newTouchedWindowHandle != nullptr) {
            if (maskedAction == AMOTION_EVENT_ACTION_HOVER_EXIT) {
                newHoverWindowHandle = nullptr;
            } else if (isHoverAction) {
                newHoverWindowHandle = newTouchedWindowHandle;
            }
        }

        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.insert(newTouchedWindows.begin(), newTouchedWindowHandle);
        }

        if (newTouchedWindows.empty()) {
            ALOGI("Dropping event because there is no touchable window at (%d, %d) on display %d.",
                  x, y, displayId);
            injectionResult = InputEventInjectionResult::FAILED;
            goto Failed;
        }

        for (const sp<WindowInfoHandle>& windowHandle : newTouchedWindows) {
            const WindowInfo& info = *windowHandle->getInfo();

            // Skip spy window targets that are not valid for targeted injection.
            if (const auto err = verifyTargetedInjection(windowHandle, entry); err) {
                continue;
            }

            if (info.inputConfig.test(WindowInfo::InputConfig::PAUSE_DISPATCHING)) {
                ALOGI("Not sending touch event to %s because it is paused",
                      windowHandle->getName().c_str());
                continue;
            }

            // Ensure the window has a connection and the connection is responsive
            const bool isResponsive = hasResponsiveConnectionLocked(*windowHandle);
            if (!isResponsive) {
                ALOGW("Not sending touch gesture to %s because it is not responsive",
                      windowHandle->getName().c_str());
                continue;
            }

            // Drop events that can't be trusted due to occlusion
            if (mBlockUntrustedTouchesMode != BlockUntrustedTouchesMode::DISABLED) {
                TouchOcclusionInfo occlusionInfo =
                        computeTouchOcclusionInfoLocked(windowHandle, x, y);
                if (!isTouchTrustedLocked(occlusionInfo)) {
                    if (DEBUG_TOUCH_OCCLUSION) {
                        ALOGD("Stack of obscuring windows during untrusted touch (%d, %d):", x, y);
                        for (const auto& log : occlusionInfo.debugInfo) {
                            ALOGD("%s", log.c_str());
                        }
                    }
                    sendUntrustedTouchCommandLocked(occlusionInfo.obscuringPackage);
                    if (mBlockUntrustedTouchesMode == BlockUntrustedTouchesMode::BLOCK) {
                        ALOGW("Dropping untrusted touch event due to %s/%d",
                              occlusionInfo.obscuringPackage.c_str(), occlusionInfo.obscuringUid);
                        continue;
                    }
                }
            }

            // Drop touch events if requested by input feature
            if (shouldDropInput(entry, windowHandle)) {
                continue;
            }

            // Set target flags.
            int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_IS;

            if (canReceiveForegroundTouches(*windowHandle->getInfo())) {
                // There should only be one touched window that can be "foreground" for the pointer.
                targetFlags |= InputTarget::FLAG_FOREGROUND;
            }

            if (isSplit) {
                targetFlags |= InputTarget::FLAG_SPLIT;
            }
            if (isWindowObscuredAtPointLocked(windowHandle, x, y)) {
                targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
            } else if (isWindowObscuredLocked(windowHandle)) {
                targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
            }

            // Update the temporary touch state.
            BitSet32 pointerIds;
            if (isSplit) {
                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;
                pointerIds.markBit(pointerId);
            }

            tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds);
        }
    } else {
        /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */

        // If the pointer is not currently down, then ignore the event.
        if (!tempTouchState.down) {
            if (DEBUG_FOCUS) {
                ALOGD("Dropping event because the pointer is not down or we previously "
                      "dropped the pointer down event in display %" PRId32,
                      displayId);
            }
            injectionResult = InputEventInjectionResult::FAILED;
            goto Failed;
        }

        addDragEventLocked(entry);

        // Check whether touches should slip outside of the current foreground window.
        if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
            tempTouchState.isSlippery()) {
            const int32_t x = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
            const int32_t y = int32_t(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));

            const bool isStylus = isPointerFromStylus(entry, 0 /*pointerIndex*/);
            sp<WindowInfoHandle> oldTouchedWindowHandle =
                    tempTouchState.getFirstForegroundWindowHandle();
            newTouchedWindowHandle =
                    findTouchedWindowAtLocked(displayId, x, y, &tempTouchState, isStylus);

            // Verify targeted injection.
            if (const auto err = verifyTargetedInjection(newTouchedWindowHandle, entry); err) {
                ALOGW("Dropping injected event: %s", (*err).c_str());
                injectionResult = os::InputEventInjectionResult::TARGET_MISMATCH;
                newTouchedWindowHandle = nullptr;
                goto Failed;
            }

            // Drop touch events if requested by input feature
            if (newTouchedWindowHandle != nullptr &&
                shouldDropInput(entry, newTouchedWindowHandle)) {
                newTouchedWindowHandle = nullptr;
            }

            if (oldTouchedWindowHandle != newTouchedWindowHandle &&
                oldTouchedWindowHandle != nullptr && newTouchedWindowHandle != nullptr) {
                if (DEBUG_FOCUS) {
                    ALOGD("Touch is slipping out of window %s into window %s in display %" PRId32,
                          oldTouchedWindowHandle->getName().c_str(),
                          newTouchedWindowHandle->getName().c_str(), displayId);
                }
                // Make a slippery exit from the old window.
                tempTouchState.addOrUpdateWindow(oldTouchedWindowHandle,
                                                 InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT,
                                                 BitSet32(0));

                // Make a slippery entrance into the new window.
                if (newTouchedWindowHandle->getInfo()->supportsSplitTouch()) {
                    isSplit = !isFromMouse;
                }

                int32_t targetFlags = InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER;
                if (canReceiveForegroundTouches(*newTouchedWindowHandle->getInfo())) {
                    targetFlags |= InputTarget::FLAG_FOREGROUND;
                }
                if (isSplit) {
                    targetFlags |= InputTarget::FLAG_SPLIT;
                }
                if (isWindowObscuredAtPointLocked(newTouchedWindowHandle, x, y)) {
                    targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED;
                } else if (isWindowObscuredLocked(newTouchedWindowHandle)) {
                    targetFlags |= InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED;
                }

                BitSet32 pointerIds;
                if (isSplit) {
                    pointerIds.markBit(entry.pointerProperties[0].id);
                }
                tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
            }
        }
    }

    // Update dispatching for hover enter and exit.
    if (newHoverWindowHandle != mLastHoverWindowHandle) {
        // Let the previous window know that the hover sequence is over, unless we already did
        // it when dispatching it as is to newTouchedWindowHandle.
        if (mLastHoverWindowHandle != nullptr &&
            (maskedAction != AMOTION_EVENT_ACTION_HOVER_EXIT ||
             mLastHoverWindowHandle != newTouchedWindowHandle)) {
            if (DEBUG_HOVER) {
                ALOGD("Sending hover exit event to window %s.",
                      mLastHoverWindowHandle->getName().c_str());
            }
            tempTouchState.addOrUpdateWindow(mLastHoverWindowHandle,
                                             InputTarget::FLAG_DISPATCH_AS_HOVER_EXIT, BitSet32(0));
        }

        // Let the new window know that the hover sequence is starting, unless we already did it
        // when dispatching it as is to newTouchedWindowHandle.
        if (newHoverWindowHandle != nullptr &&
            (maskedAction != AMOTION_EVENT_ACTION_HOVER_ENTER ||
             newHoverWindowHandle != newTouchedWindowHandle)) {
            if (DEBUG_HOVER) {
                ALOGD("Sending hover enter event to window %s.",
                      newHoverWindowHandle->getName().c_str());
            }
            tempTouchState.addOrUpdateWindow(newHoverWindowHandle,
                                             InputTarget::FLAG_DISPATCH_AS_HOVER_ENTER,
                                             BitSet32(0));
        }
    }

    // Ensure that we have at least one foreground window or at least one window that cannot be a
    // foreground target. If we only have windows that are not receiving foreground touches (e.g. we
    // only have windows getting ACTION_OUTSIDE), then drop the event, because there is no window
    // that is actually receiving the entire gesture.
    if (std::none_of(tempTouchState.windows.begin(), tempTouchState.windows.end(),
                     [](const TouchedWindow& touchedWindow) {
                         return !canReceiveForegroundTouches(
                                        *touchedWindow.windowHandle->getInfo()) ||
                                 (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND) != 0;
                     })) {
        ALOGI("Dropping event because there is no touched window on display %d to receive it: %s",
              displayId, entry.getDescription().c_str());
        injectionResult = InputEventInjectionResult::FAILED;
        goto Failed;
    }

    // Ensure that all touched windows are valid for injection.
    if (entry.injectionState != nullptr) {
        std::string errs;
        for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
            if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
                // Allow ACTION_OUTSIDE events generated by targeted injection to be
                // dispatched to any uid, since the coords will be zeroed out later.
                continue;
            }
            const auto err = verifyTargetedInjection(touchedWindow.windowHandle, entry);
            if (err) errs += "\n  - " + *err;
        }
        if (!errs.empty()) {
            ALOGW("Dropping targeted injection: At least one touched window is not owned by uid "
                  "%d:%s",
                  *entry.injectionState->targetUid, errs.c_str());
            injectionResult = InputEventInjectionResult::TARGET_MISMATCH;
            goto Failed;
        }
    }

    // Check whether windows listening for outside touches are owned by the same UID. If it is
    // set the policy flag that we will not reveal coordinate information to this window.
    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
        sp<WindowInfoHandle> foregroundWindowHandle =
                tempTouchState.getFirstForegroundWindowHandle();
        if (foregroundWindowHandle) {
            const int32_t foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
            for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
                if (touchedWindow.targetFlags & InputTarget::FLAG_DISPATCH_AS_OUTSIDE) {
                    sp<WindowInfoHandle> windowInfoHandle = touchedWindow.windowHandle;
                    if (windowInfoHandle->getInfo()->ownerUid != foregroundWindowUid) {
                        tempTouchState.addOrUpdateWindow(windowInfoHandle,
                                                         InputTarget::FLAG_ZERO_COORDS,
                                                         BitSet32(0));
                    }
                }
            }
        }
    }

    // If this is the first 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 (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
        sp<WindowInfoHandle> foregroundWindowHandle =
                tempTouchState.getFirstForegroundWindowHandle();
        if (foregroundWindowHandle &&
            foregroundWindowHandle->getInfo()->inputConfig.test(
                    WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
            const std::vector<sp<WindowInfoHandle>>& windowHandles =
                    getWindowHandlesLocked(displayId);
            for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
                const WindowInfo* info = windowHandle->getInfo();
                if (info->displayId == displayId &&
                    windowHandle->getInfo()->inputConfig.test(
                            WindowInfo::InputConfig::IS_WALLPAPER)) {
                    tempTouchState
                            .addOrUpdateWindow(windowHandle,
                                               InputTarget::FLAG_WINDOW_IS_OBSCURED |
                                                       InputTarget::
                                                               FLAG_WINDOW_IS_PARTIALLY_OBSCURED |
                                                       InputTarget::FLAG_DISPATCH_AS_IS,
                                               BitSet32(0));
                }
            }
        }
    }

    // Success!  Output targets.
    injectionResult = InputEventInjectionResult::SUCCEEDED;

    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                              touchedWindow.pointerIds, inputTargets);
    }

    // Drop the outside or hover touch windows since we will not care about them
    // in the next iteration.
    tempTouchState.filterNonAsIsTouchWindows();

Failed:
    // Update final pieces of touch state if the injector had permission.
    if (!wrongDevice) {
        if (switchedDevice) {
            if (DEBUG_FOCUS) {
                ALOGD("Conflicting pointer actions: Switched to a different device.");
            }
            *outConflictingPointerActions = true;
        }

        if (isHoverAction) {
            // Started hovering, therefore no longer down.
            if (oldState && oldState->down) {
                if (DEBUG_FOCUS) {
                    ALOGD("Conflicting pointer actions: Hover received while pointer was "
                          "down.");
                }
                *outConflictingPointerActions = true;
            }
            tempTouchState.reset();
            if (maskedAction == AMOTION_EVENT_ACTION_HOVER_ENTER ||
                maskedAction == AMOTION_EVENT_ACTION_HOVER_MOVE) {
                tempTouchState.deviceId = entry.deviceId;
                tempTouchState.source = entry.source;
                tempTouchState.displayId = displayId;
            }
        } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
                   maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
            // All pointers up or canceled.
            tempTouchState.reset();
        } else if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
            // First pointer went down.
            if (oldState && oldState->down) {
                if (DEBUG_FOCUS) {
                    ALOGD("Conflicting pointer actions: Down received while already down.");
                }
                *outConflictingPointerActions = true;
            }
        } else if (maskedAction == AMOTION_EVENT_ACTION_POINTER_UP) {
            // One pointer went up.
            if (isSplit) {
                int32_t pointerIndex = getMotionEventActionPointerIndex(action);
                uint32_t pointerId = entry.pointerProperties[pointerIndex].id;

                for (size_t i = 0; i < tempTouchState.windows.size();) {
                    TouchedWindow& touchedWindow = tempTouchState.windows[i];
                    if (touchedWindow.targetFlags & InputTarget::FLAG_SPLIT) {
                        touchedWindow.pointerIds.clearBit(pointerId);
                        if (touchedWindow.pointerIds.isEmpty()) {
                            tempTouchState.windows.erase(tempTouchState.windows.begin() + i);
                            continue;
                        }
                    }
                    i += 1;
                }
            }
        }

        // Save changes unless the action was scroll in which case the temporary touch
        // state was only valid for this one action.
        if (maskedAction != AMOTION_EVENT_ACTION_SCROLL) {
            if (tempTouchState.displayId >= 0) {
                mTouchStatesByDisplay[displayId] = tempTouchState;
            } else {
                mTouchStatesByDisplay.erase(displayId);
            }
        }

        // Update hover state.
        mLastHoverWindowHandle = newHoverWindowHandle;
    }

    return injectionResult;
}

InputDispatcher::findTouchedWindowAtLocked

sp<WindowInfoHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
                                                                int32_t y, TouchState* touchState,
                                                                bool isStylus,
                                                                bool addOutsideTargets,
                                                                bool ignoreDragWindow) {
    if (addOutsideTargets && touchState == nullptr) {
        LOG_ALWAYS_FATAL("Must provide a valid touch state if adding outside targets");
    }
    // Traverse windows from front to back to find touched window.
    const auto& windowHandles = getWindowHandlesLocked(displayId);
    for (const sp<WindowInfoHandle>& windowHandle : windowHandles) {
        if (ignoreDragWindow && haveSameToken(windowHandle, mDragState->dragWindow)) {
            continue;
        }

        const WindowInfo& info = *windowHandle->getInfo();
        if (!info.isSpy() && windowAcceptsTouchAt(info, displayId, x, y, isStylus)) {
            return windowHandle;
        }

        if (addOutsideTargets &&
            info.inputConfig.test(WindowInfo::InputConfig::WATCH_OUTSIDE_TOUCH)) {
            touchState->addOrUpdateWindow(windowHandle, InputTarget::FLAG_DISPATCH_AS_OUTSIDE,
                                          BitSet32(0));
        }
    }
    return nullptr;
}

InputDispatcher::getWindowHandlesLocked

const std::vector<sp<WindowInfoHandle>>& InputDispatcher::getWindowHandlesLocked(
        int32_t displayId) const {
    static const std::vector<sp<WindowInfoHandle>> EMPTY_WINDOW_HANDLES;
    auto it = mWindowHandlesByDisplay.find(displayId);
    return it != mWindowHandlesByDisplay.end() ? it->second : EMPTY_WINDOW_HANDLES;
}

mWindowHandlesByDisplay又是谁来添加插入数据的

void InputDispatcher::updateWindowHandlesForDisplayLocked(
        const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
    if (windowInfoHandles.empty()) {
        // Remove all handles on a display if there are no windows left.
        mWindowHandlesByDisplay.erase(displayId);
        return;
    }

    // Since we compare the pointer of input window handles across window updates, we need
    // to make sure the handle object for the same window stays unchanged across updates.
    const std::vector<sp<WindowInfoHandle>>& oldHandles = getWindowHandlesLocked(displayId);
    std::unordered_map<int32_t /*id*/, sp<WindowInfoHandle>> oldHandlesById;
    for (const sp<WindowInfoHandle>& handle : oldHandles) {
        oldHandlesById[handle->getId()] = handle;
    }

    std::vector<sp<WindowInfoHandle>> newHandles;
    for (const sp<WindowInfoHandle>& handle : windowInfoHandles) {
        const WindowInfo* info = handle->getInfo();
        if (getInputChannelLocked(handle->getToken()) == nullptr) {
            const bool noInputChannel =
                    info->inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
            const bool canReceiveInput =
                    !info->inputConfig.test(WindowInfo::InputConfig::NOT_TOUCHABLE) ||
                    !info->inputConfig.test(WindowInfo::InputConfig::NOT_FOCUSABLE);
            if (canReceiveInput && !noInputChannel) {
                ALOGV("Window handle %s has no registered input channel",
                      handle->getName().c_str());
                continue;
            }
        }

        if (info->displayId != displayId) {
            ALOGE("Window %s updated by wrong display %d, should belong to display %d",
                  handle->getName().c_str(), displayId, info->displayId);
            continue;
        }

        if ((oldHandlesById.find(handle->getId()) != oldHandlesById.end()) &&
                (oldHandlesById.at(handle->getId())->getToken() == handle->getToken())) {
            const sp<WindowInfoHandle>& oldHandle = oldHandlesById.at(handle->getId());
            oldHandle->updateFrom(handle);
            newHandles.push_back(oldHandle);
        } else {
            newHandles.push_back(handle);
        }
    }

    // Insert or replace
    // 将<displayId, newHandles>插入mWindowHandlesByDisplay
    mWindowHandlesByDisplay[displayId] = newHandles;
}

谁调用的updateWindowHandlesForDisplayLocked

InputDispatcher::setInputWindowsLocked

void InputDispatcher::setInputWindowsLocked(
        const std::vector<sp<WindowInfoHandle>>& windowInfoHandles, int32_t displayId) {
    if (DEBUG_FOCUS) {
        std::string windowList;
        for (const sp<WindowInfoHandle>& iwh : windowInfoHandles) {
            windowList += iwh->getName() + " ";
        }
        ALOGD("setInputWindows displayId=%" PRId32 " %s", displayId, windowList.c_str());
    }

    // Check preconditions for new input windows
    for (const sp<WindowInfoHandle>& window : windowInfoHandles) {
        const WindowInfo& info = *window->getInfo();

        // Ensure all tokens are null if the window has feature NO_INPUT_CHANNEL
        const bool noInputWindow = info.inputConfig.test(WindowInfo::InputConfig::NO_INPUT_CHANNEL);
        if (noInputWindow && window->getToken() != nullptr) {
            ALOGE("%s has feature NO_INPUT_WINDOW, but a non-null token. Clearing",
                  window->getName().c_str());
            window->releaseChannel();
        }

        // Ensure all spy windows are trusted overlays
        LOG_ALWAYS_FATAL_IF(info.isSpy() &&
                                    !info.inputConfig.test(
                                            WindowInfo::InputConfig::TRUSTED_OVERLAY),
                            "%s has feature SPY, but is not a trusted overlay.",
                            window->getName().c_str());

        // Ensure all stylus interceptors are trusted overlays
        LOG_ALWAYS_FATAL_IF(info.interceptsStylus() &&
                                    !info.inputConfig.test(
                                            WindowInfo::InputConfig::TRUSTED_OVERLAY),
                            "%s has feature INTERCEPTS_STYLUS, but is not a trusted overlay.",
                            window->getName().c_str());
    }

    // Copy old handles for release if they are no longer present.
    const std::vector<sp<WindowInfoHandle>> oldWindowHandles = getWindowHandlesLocked(displayId);

    // Save the old windows' orientation by ID before it gets updated.
    std::unordered_map<int32_t, uint32_t> oldWindowOrientations;
    for (const sp<WindowInfoHandle>& handle : oldWindowHandles) {
        oldWindowOrientations.emplace(handle->getId(),
                                      handle->getInfo()->transform.getOrientation());
    }

    updateWindowHandlesForDisplayLocked(windowInfoHandles, displayId);  //更新并插入mWindowHandlesByDisplay

    const std::vector<sp<WindowInfoHandle>>& windowHandles = getWindowHandlesLocked(displayId);
    if (mLastHoverWindowHandle &&
        std::find(windowHandles.begin(), windowHandles.end(), mLastHoverWindowHandle) ==
                windowHandles.end()) {
        mLastHoverWindowHandle = nullptr;
    }

    std::optional<FocusResolver::FocusChanges> changes =
            mFocusResolver.setInputWindows(displayId, windowHandles);
    if (changes) {
        onFocusChangedLocked(*changes);
    }

    std::unordered_map<int32_t, TouchState>::iterator stateIt =
            mTouchStatesByDisplay.find(displayId);
    if (stateIt != mTouchStatesByDisplay.end()) {
        TouchState& state = stateIt->second;
        for (size_t i = 0; i < state.windows.size();) {
            TouchedWindow& touchedWindow = state.windows[i];
            if (getWindowHandleLocked(touchedWindow.windowHandle) == nullptr) {
                if (DEBUG_FOCUS) {
                    ALOGD("Touched window was removed: %s in display %" PRId32,
                          touchedWindow.windowHandle->getName().c_str(), displayId);
                }
                std::shared_ptr<InputChannel> touchedInputChannel =
                        getInputChannelLocked(touchedWindow.windowHandle->getToken());
                if (touchedInputChannel != nullptr) {
                    CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                                               "touched window was removed");
                    synthesizeCancelationEventsForInputChannelLocked(touchedInputChannel, options);
                    // Since we are about to drop the touch, cancel the events for the wallpaper as
                    // well.
                    if (touchedWindow.targetFlags & InputTarget::FLAG_FOREGROUND &&
                        touchedWindow.windowHandle->getInfo()->inputConfig.test(
                                gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) {
                        sp<WindowInfoHandle> wallpaper = state.getWallpaperWindow();
                        if (wallpaper != nullptr) {
                            sp<Connection> wallpaperConnection =
                                    getConnectionLocked(wallpaper->getToken());
                            if (wallpaperConnection != nullptr) {
                                synthesizeCancelationEventsForConnectionLocked(wallpaperConnection,
                                                                               options);
                            }
                        }
                    }
                }
                state.windows.erase(state.windows.begin() + i);
            } else {
                ++i;
            }
        }

        // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. We
        // could just clear the state here.
        if (mDragState && mDragState->dragWindow->getInfo()->displayId == displayId &&
            std::find(windowHandles.begin(), windowHandles.end(), mDragState->dragWindow) ==
                    windowHandles.end()) {
            ALOGI("Drag window went away: %s", mDragState->dragWindow->getName().c_str());
            sendDropWindowCommandLocked(nullptr, 0, 0);
            mDragState.reset();
        }
    }

    // Determine if the orientation of any of the input windows have changed, and cancel all
    // pointer events if necessary.
    for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
        const sp<WindowInfoHandle> newWindowHandle = getWindowHandleLocked(oldWindowHandle);
        if (newWindowHandle != nullptr &&
            newWindowHandle->getInfo()->transform.getOrientation() !=
                    oldWindowOrientations[oldWindowHandle->getId()]) {
            std::shared_ptr<InputChannel> inputChannel =
                    getInputChannelLocked(newWindowHandle->getToken());
            if (inputChannel != nullptr) {
                CancelationOptions options(CancelationOptions::CANCEL_POINTER_EVENTS,
                                           "touched window's orientation changed");
                synthesizeCancelationEventsForInputChannelLocked(inputChannel, options);
            }
        }
    }

    // Release information for windows that are no longer present.
    // This ensures that unused input channels are released promptly.
    // Otherwise, they might stick around until the window handle is destroyed
    // which might not happen until the next GC.
    for (const sp<WindowInfoHandle>& oldWindowHandle : oldWindowHandles) {
        if (getWindowHandleLocked(oldWindowHandle) == nullptr) {
            if (DEBUG_FOCUS) {
                ALOGD("Window went away: %s", oldWindowHandle->getName().c_str());
            }
            oldWindowHandle->releaseChannel();
        }
    }
}

谁来调用setInputWindowsLocked, 主要是两个地方:

  1. InputDispatcher::onWindowInfosChanged
  2. InputDispatcher::displayRemoved

途径一

InputDispatcher::onWindowInfosChanged
void InputDispatcher::onWindowInfosChanged(const std::vector<WindowInfo>& windowInfos,
                                           const std::vector<DisplayInfo>& displayInfos) {
    // The listener sends the windows as a flattened array. Separate the windows by display for
    // more convenient parsing.
    std::unordered_map<int32_t, std::vector<sp<WindowInfoHandle>>> handlesPerDisplay;
    for (const auto& info : windowInfos) {
        handlesPerDisplay.emplace(info.displayId, std::vector<sp<WindowInfoHandle>>());
        handlesPerDisplay[info.displayId].push_back(new WindowInfoHandle(info));
    }

    { // acquire lock
        std::scoped_lock _l(mLock);
        mDisplayInfos.clear();
        for (const auto& displayInfo : displayInfos) {
            mDisplayInfos.emplace(displayInfo.displayId, displayInfo);
        }

        for (const auto& [displayId, handles] : handlesPerDisplay) {
            setInputWindowsLocked(handles, displayId);
        }
    }
    // Wake up poll loop since it may need to make new input dispatching choices.
    // 唤醒dispatcher线程,处理窗口信息改变的相关事项
    mLooper->wake();
}

寻找onWindowInfosChanged的调用地方

InputDispatcher::DispatcherWindowListener::onWindowInfosChanged
void InputDispatcher::DispatcherWindowListener::onWindowInfosChanged(
        const std::vector<gui::WindowInfo>& windowInfos,
        const std::vector<DisplayInfo>& displayInfos) {
    mDispatcher.onWindowInfosChanged(windowInfos, displayInfos);
}

看起来是个监听器的回调,应该是给别的地方持有这个监听器,寻找下这个DispatcherWindowListener的实例化

InputDispatcher::InputDispatcher
InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy,
                                 std::chrono::nanoseconds staleEventTimeout)
      : mPolicy(policy),
        mPendingEvent(nullptr),
        mLastDropReason(DropReason::NOT_DROPPED),
        mIdGenerator(IdGenerator::Source::INPUT_DISPATCHER),
        mAppSwitchSawKeyDown(false),
        mAppSwitchDueTime(LONG_LONG_MAX),
        mNextUnblockedEvent(nullptr),
        mMonitorDispatchingTimeout(DEFAULT_INPUT_DISPATCHING_TIMEOUT),
        mDispatchEnabled(false),
        mDispatchFrozen(false),
        mInputFilterEnabled(false),
        // mInTouchMode will be initialized by the WindowManager to the default device config.
        // To avoid leaking stack in case that call never comes, and for tests,
        // initialize it here anyways.
        mInTouchMode(kDefaultInTouchMode),
        mMaximumObscuringOpacityForTouch(1.0f),
        mFocusedDisplayId(ADISPLAY_ID_DEFAULT),
        mWindowTokenWithPointerCapture(nullptr),
        mStaleEventTimeout(staleEventTimeout),
        mLatencyAggregator(),
        mLatencyTracker(&mLatencyAggregator) {
    mLooper = new Looper(false);
    mReporter = createInputReporter();
	//实例化DispatcherWindowListener,并通过binder传给SurfaceComposerClient,也就是SurfaceFlinger
    mWindowInfoListener = new DispatcherWindowListener(*this);
    SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);

	...
}

到这里我们明白Android 13通过途径1(InputDispatcher::onWindowInfosChanged)来更新触摸窗口信息的发起者是SurfaceFlinger

SurfaceFlinger::addWindowInfosListener

frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp

status_t SurfaceFlinger::addWindowInfosListener(
        const sp<IWindowInfosListener>& windowInfosListener) const {
    mWindowInfosListenerInvoker->addWindowInfosListener(windowInfosListener);
    return NO_ERROR;
}
WindowInfosListenerInvoker::addWindowInfosListener
void WindowInfosListenerInvoker::addWindowInfosListener(sp<IWindowInfosListener> listener) {
    sp<IBinder> asBinder = IInterface::asBinder(listener);
    asBinder->linkToDeath(this);

    std::scoped_lock lock(mListenersMutex);
    mWindowInfosListeners.try_emplace(asBinder, std::move(listener));
}

inputdispatcher实例化时调用上面流程跨进程向SurfaceFlinger注册了DispatcherWindowListener, 回到上面,我们找到回调这个监听器的onWindowInfosChanged的地方

WindowInfosListenerInvoker::windowInfosChanged

frameworks/native/services/surfaceflinger/WindowInfosListenerInvoker.cpp

void WindowInfosListenerInvoker::windowInfosChanged(const std::vector<WindowInfo>& windowInfos,
                                                    const std::vector<DisplayInfo>& displayInfos,
                                                    bool shouldSync) {
    ftl::SmallVector<const sp<IWindowInfosListener>, kStaticCapacity> windowInfosListeners;
    {
        std::scoped_lock lock(mListenersMutex);
        for (const auto& [_, listener] : mWindowInfosListeners) {
            windowInfosListeners.push_back(listener);
        }
    }

    mCallbacksPending = windowInfosListeners.size();

    for (const auto& listener : windowInfosListeners) {
    	//向所有向SurfaceFlinger注册了WindowInfosListener的进程回调onWindowInfosChanged
    	//其中包括inputDispatcher中的onWindowInfosChanged
        listener->onWindowInfosChanged(windowInfos, displayInfos,
                                       shouldSync ? mWindowInfosReportedListener : nullptr);
    }
}

到这里途径1(InputDispatcher::onWindowInfosChanged)来更新触摸窗口信息的流程就找到了。

途径二

InputDispatcher::displayRemoved
void InputDispatcher::displayRemoved(int32_t displayId) {
    { // acquire lock
        std::scoped_lock _l(mLock);
        // Set an empty list to remove all handles from the specific display.
        setInputWindowsLocked(/* window handles */ {}, displayId);
        setFocusedApplicationLocked(displayId, nullptr);
        // Call focus resolver to clean up stale requests. This must be called after input windows
        // have been removed for the removed display.
        mFocusResolver.displayRemoved(displayId);
        // Reset pointer capture eligibility, regardless of previous state.
        std::erase(mIneligibleDisplaysForPointerCapture, displayId);
    } // release lock

    // Wake up poll loop since it may need to make new input dispatching choices.
    mLooper->wake();
}
NativeInputManager::displayRemoved

frameworks/base/services/core/jni/com_android_server_input_InputManagerService.cpp

void NativeInputManager::displayRemoved(JNIEnv* env, int32_t displayId) {
    mInputManager->getDispatcher().displayRemoved(displayId);
}
nativeDisplayRemoved
static const JNINativeMethod gInputManagerMethods[] = {
        /* name, signature, funcPtr */
        ...
      {"displayRemoved", "(I)V", (void*)nativeDisplayRemoved},  //动态注册函数表
};

static void nativeDisplayRemoved(JNIEnv* env, jobject nativeImplObj, jint displayId) {
    NativeInputManager* im = getNativeInputManager(env, nativeImplObj);

    im->displayRemoved(env, displayId);
}

看这里就知道是java层调下来的, 那就到java层找找

NativeInputManagerService::displayRemoved

frameworks/base/services/core/java/com/android/server/input/NativeInputManagerService.java

public interface NativeInputManagerService {
	...
    void displayRemoved(int displayId);
	...
    class NativeImpl implements NativeInputManagerService {
    	...
        public native void displayRemoved(int displayId);
        ...
    }
}
InputManagerService::onDisplayRemoved

frameworks/base/services/core/java/com/android/server/input/InputManagerService.java

/** The system implementation of {@link IInputManager} that manages input devices. */
public class InputManagerService extends IInputManager.Stub
        implements Watchdog.Monitor {

	...
    /** Clean up input window handles of the given display. */
    public void onDisplayRemoved(int displayId) {
        if (mPointerIconDisplayContext != null
                && mPointerIconDisplayContext.getDisplay().getDisplayId() == displayId) {
            mPointerIconDisplayContext = null;
        }

        updateAdditionalDisplayInputProperties(displayId, AdditionalDisplayInputProperties::reset);

        mNative.displayRemoved(displayId);
    }
    ...
}
InputMonitor::onDisplayRemoved

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

    void onDisplayRemoved() {
        mHandler.removeCallbacks(mUpdateInputWindows);
        mHandler.post(() -> {
            // Make sure any pending setInputWindowInfo transactions are completed. That prevents
            // the timing of updating input info of removed display after cleanup.
            mService.mTransactionFactory.get().syncInputWindows().apply();
            // It calls InputDispatcher::setInputWindows directly.
            //mService是WindowManagerService,构造时向它传入了java层InputManagerService对象
            mService.mInputManager.onDisplayRemoved(mDisplayId);
        });
        mDisplayRemoved = true;
    }
DisplayContent::removeImmediately

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

    @Override
    void removeImmediately() {
        mDeferredRemoval = false;
        try {
            // Clear all transitions & screen frozen states when removing display.
			...
            mInputMonitor.onDisplayRemoved();
			...
        } finally {
            mDisplayReady = false;
        }

        // Apply the pending transaction here since we may not be able to reach the DisplayContent
        // on the next traversal if it's removed from RootWindowContainer child list.
        getPendingTransaction().apply();
        mWmService.mWindowPlacerLocked.requestTraversal();
    }

到这里差不多了,再往上查就是WMS对窗口管理的内容了,不是本次探索的目的,到这里就明白当事件分发前寻找的触摸目标窗口信息是谁来告知的。

  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
el-input-number 是 element-ui 中的一个组件,用于输入数字。要获取 el-input-number 的值,可以使用 v-model 指令绑定一个变量来实现双向绑定。在你提供的代码中,可以通过 option.startValue 来获取 el-input-number 的值。当 el-input-number 的值发生变化时,会触发 @change 事件,你可以在 handleChangeStartValue 方法中处理新的值。在这个方法中,你可以将新的值赋给 option.startValue 来更新变量的值。所以,要获取 el-input-number 的值,可以通过访问 option.startValue 来获取。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [设置 el-input-number 整数取值](https://blog.csdn.net/weixin_42274697/article/details/121062994)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [vue-limit-input-group:vue联合输入限制控件el-input-number增强](https://download.csdn.net/download/weixin_42164685/15512269)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [vue-number-input:Vue.js的数字输入组件](https://download.csdn.net/download/weixin_42138376/16605965)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

坂田民工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值