Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ccf6ce3d authored by Linnan Li's avatar Linnan Li Committed by Siarhei Vishniakou
Browse files

Associate device id with getFirstForegroundWindowHandle



Because we have supported multi-device event streams in the
InputDispatcher, but there are still some logics that use the previous
single-device stream logic. Here, associating
getFirstForegroundWindowHandle with deviceId can solve problems such as
events being dispatched to the wrong window, and windows with the same
owner listening to ACTION_OUTSIDE events without coordinates.

Bug: 328553381
Test: atest inputflinger_tests

Change-Id: I71ca90f7f7a6aa05d746fc8d3f0e0fe69d45294b
Signed-off-by: default avatarLinnan Li <lilinnan@xiaomi.corp-partner.google.com>
parent bc656056
Loading
Loading
Loading
Loading
+3 −3
Original line number Diff line number Diff line
@@ -2455,7 +2455,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
        if (newTouchedWindowHandle == nullptr) {
            ALOGD("No new touched window at (%.1f, %.1f) 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();
            newTouchedWindowHandle = tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
        }

        // Verify targeted injection.
@@ -2623,7 +2623,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
            const auto [x, y] = resolveTouchedPosition(entry);
            const bool isStylus = isPointerFromStylus(entry, /*pointerIndex=*/0);
            sp<WindowInfoHandle> oldTouchedWindowHandle =
                    tempTouchState.getFirstForegroundWindowHandle();
                    tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
            LOG_ALWAYS_FATAL_IF(oldTouchedWindowHandle == nullptr);
            sp<WindowInfoHandle> newTouchedWindowHandle =
                    findTouchedWindowAtLocked(displayId, x, y, isStylus);
@@ -2741,7 +2741,7 @@ std::vector<InputTarget> InputDispatcher::findTouchedWindowTargetsLocked(
    // has a different UID, then we will not reveal coordinate information to this window.
    if (maskedAction == AMOTION_EVENT_ACTION_DOWN) {
        sp<WindowInfoHandle> foregroundWindowHandle =
                tempTouchState.getFirstForegroundWindowHandle();
                tempTouchState.getFirstForegroundWindowHandle(entry.deviceId);
        if (foregroundWindowHandle) {
            const auto foregroundWindowUid = foregroundWindowHandle->getInfo()->ownerUid;
            for (InputTarget& target : targets) {
+5 −3
Original line number Diff line number Diff line
@@ -180,9 +180,11 @@ void TouchState::cancelPointersForNonPilferingWindows() {
    clearWindowsWithoutPointers();
}

sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle() const {
    for (size_t i = 0; i < windows.size(); i++) {
        const TouchedWindow& window = windows[i];
sp<WindowInfoHandle> TouchState::getFirstForegroundWindowHandle(DeviceId deviceId) const {
    for (const auto& window : windows) {
        if (!window.hasTouchingPointers(deviceId)) {
            continue;
        }
        if (window.targetFlags.test(InputTarget::Flags::FOREGROUND)) {
            return window.windowHandle;
        }
+1 −1
Original line number Diff line number Diff line
@@ -64,7 +64,7 @@ struct TouchState {
    // set to false.
    void cancelPointersForNonPilferingWindows();

    sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle() const;
    sp<android::gui::WindowInfoHandle> getFirstForegroundWindowHandle(DeviceId deviceId) const;
    bool isSlippery() const;
    sp<android::gui::WindowInfoHandle> getWallpaperWindow() const;
    const TouchedWindow& getTouchedWindow(
+144 −0
Original line number Diff line number Diff line
@@ -4799,6 +4799,96 @@ TEST_F(InputDispatcherTest, ActionOutsideForOwnedWindowHasValidCoordinates) {
    outsideWindow->assertNoEvents();
}
/**
 * Three windows:
 * - Left window
 * - Right window
 * - Outside window(watch for ACTION_OUTSIDE events)
 * The windows "left" and "outside" share the same owner, the window "right" has a different owner,
 * In order to allow the outside window can receive the ACTION_OUTSIDE events, the outside window is
 * positioned above the "left" and "right" windows, and it doesn't overlap with them.
 *
 * First, device A report a down event landed in the right window, the outside window can receive
 * an ACTION_OUTSIDE event that with zeroed coordinates, the device B report a down event landed
 * in the left window, the outside window can receive an ACTION_OUTSIDE event the with valid
 * coordinates, after these, device A and device B continue report MOVE event, the right and left
 * window can receive it, but outside window event can't receive it.
 */
TEST_F(InputDispatcherTest, ActionOutsideForOwnedWindowHasValidCoordinatesWhenMultiDevice) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> leftWindow =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Left Window",
                                       ADISPLAY_ID_DEFAULT);
    leftWindow->setFrame(Rect{0, 0, 100, 100});
    leftWindow->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
    sp<FakeWindowHandle> outsideWindow =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Outside Window",
                                       ADISPLAY_ID_DEFAULT);
    outsideWindow->setFrame(Rect{100, 100, 200, 200});
    outsideWindow->setOwnerInfo(gui::Pid{1}, gui::Uid{101});
    outsideWindow->setWatchOutsideTouch(true);
    std::shared_ptr<FakeApplicationHandle> anotherApplication =
            std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> rightWindow =
            sp<FakeWindowHandle>::make(anotherApplication, mDispatcher, "Right Window",
                                       ADISPLAY_ID_DEFAULT);
    rightWindow->setFrame(Rect{100, 0, 200, 100});
    rightWindow->setOwnerInfo(gui::Pid{2}, gui::Uid{202});
    // OutsideWindow must be above left window and right window to receive ACTION_OUTSIDE events
    // when left window or right window is tapped
    mDispatcher->onWindowInfosChanged(
            {{*outsideWindow->getInfo(), *leftWindow->getInfo(), *rightWindow->getInfo()},
             {},
             0,
             0});
    const DeviceId deviceA = 9;
    const DeviceId deviceB = 3;
    // Tap on right window use device A
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
                                      .deviceId(deviceA)
                                      .build());
    leftWindow->assertNoEvents();
    rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
    // Right window is belonged to another owner, so outsideWindow should receive ACTION_OUTSIDE
    // with zeroed coords.
    outsideWindow->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_OUTSIDE), WithDeviceId(deviceA), WithCoords(0, 0)));
    // Tap on left window use device B
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
                                      .deviceId(deviceB)
                                      .build());
    leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
    rightWindow->assertNoEvents();
    // Because new gesture down on the left window that has the same owner with outside Window, the
    // outside Window should receive the ACTION_OUTSIDE with coords.
    outsideWindow->consumeMotionEvent(
            AllOf(WithMotionAction(ACTION_OUTSIDE), WithDeviceId(deviceB), WithCoords(-50, -50)));
    // Ensure that windows that can only accept outside do not receive remaining gestures
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
                                      .deviceId(deviceA)
                                      .build());
    leftWindow->assertNoEvents();
    rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceA)));
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(51).y(51))
                                      .deviceId(deviceB)
                                      .build());
    leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceB)));
    rightWindow->assertNoEvents();
    outsideWindow->assertNoEvents();
}
/**
 * This test documents the behavior of WATCH_OUTSIDE_TOUCH. The window will get ACTION_OUTSIDE when
 * a another pointer causes ACTION_DOWN to be sent to another window for the first time. Only one
@@ -7391,6 +7481,60 @@ TEST_F(InputDispatcherTest, HoverEnterExitSynthesisUsesNewEventId) {
    spy->consumeMotionEvent(AllOf(WithMotionAction(ACTION_HOVER_MOVE), WithEventId(notifyArgs.id)));
}
/**
 * When a device reports a DOWN event, which lands in a window that supports splits, and then the
 * device then reports a POINTER_DOWN, which lands in the location of a non-existing window, then
 * the previous window should receive this event and not be dropped.
 */
TEST_F(InputDispatcherMultiDeviceTest, SingleDevicePointerDownEventRetentionWithoutWindowTarget) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
    window->setFrame(Rect(0, 0, 100, 100));
    mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
                                      .build());
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
    mDispatcher->notifyMotion(MotionArgsBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
                                      .pointer(PointerBuilder(1, ToolType::FINGER).x(200).y(200))
                                      .build());
    window->consumeMotionEvent(AllOf(WithMotionAction(POINTER_1_DOWN)));
}
/**
 * When deviceA reports a DOWN event, which lands in a window that supports splits, and then deviceB
 * also reports a DOWN event, which lands in the location of a non-existing window, then the
 * previous window should receive deviceB's event and it should be dropped.
 */
TEST_F(InputDispatcherMultiDeviceTest, SecondDeviceDownEventDroppedWithoutWindowTarget) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, mDispatcher, "Window", ADISPLAY_ID_DEFAULT);
    window->setFrame(Rect(0, 0, 100, 100));
    mDispatcher->onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});
    const DeviceId deviceA = 9;
    const DeviceId deviceB = 3;
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
                                      .deviceId(deviceA)
                                      .build());
    window->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
    mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
                                      .pointer(PointerBuilder(0, ToolType::FINGER).x(200).y(200))
                                      .deviceId(deviceB)
                                      .build());
    window->assertNoEvents();
}
class InputDispatcherFallbackKeyTest : public InputDispatcherTest {
protected:
    std::shared_ptr<FakeApplicationHandle> mApp;