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

Commit eb4065a4 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Automerger Merge Worker
Browse files

Merge "Handle multiple windows in transferTouch" into tm-dev am: d16a2ea9

parents 5dc854ad d16a2ea9
Loading
Loading
Loading
Loading
+39 −13
Original line number Diff line number Diff line
@@ -5146,28 +5146,54 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<
    return true;
}

/**
 * Get the touched foreground window on the given display.
 * Return null if there are no windows touched on that display, or if more than one foreground
 * window is being touched.
 */
sp<WindowInfoHandle> InputDispatcher::findTouchedForegroundWindowLocked(int32_t displayId) const {
    auto stateIt = mTouchStatesByDisplay.find(displayId);
    if (stateIt == mTouchStatesByDisplay.end()) {
        ALOGI("No touch state on display %" PRId32, displayId);
        return nullptr;
    }

    const TouchState& state = stateIt->second;
    sp<WindowInfoHandle> touchedForegroundWindow;
    // If multiple foreground windows are touched, return nullptr
    for (const TouchedWindow& window : state.windows) {
        if (window.targetFlags & InputTarget::FLAG_FOREGROUND) {
            if (touchedForegroundWindow != nullptr) {
                ALOGI("Two or more foreground windows: %s and %s",
                      touchedForegroundWindow->getName().c_str(),
                      window.windowHandle->getName().c_str());
                return nullptr;
            }
            touchedForegroundWindow = window.windowHandle;
        }
    }
    return touchedForegroundWindow;
}

// Binder call
bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken) {
bool InputDispatcher::transferTouch(const sp<IBinder>& destChannelToken, int32_t displayId) {
    sp<IBinder> fromToken;
    { // acquire lock
        std::scoped_lock _l(mLock);

        auto it = std::find_if(mTouchStatesByDisplay.begin(), mTouchStatesByDisplay.end(),
                               [](const auto& pair) { return pair.second.windows.size() == 1; });
        if (it == mTouchStatesByDisplay.end()) {
            ALOGW("Cannot transfer touch state because there is no exact window being touched");
            return false;
        }
        const int32_t displayId = it->first;
        sp<WindowInfoHandle> toWindowHandle = getWindowHandleLocked(destChannelToken, displayId);
        if (toWindowHandle == nullptr) {
            ALOGW("Could not find window associated with token=%p", destChannelToken.get());
            ALOGW("Could not find window associated with token=%p on display %" PRId32,
                  destChannelToken.get(), displayId);
            return false;
        }

        sp<WindowInfoHandle> from = findTouchedForegroundWindowLocked(displayId);
        if (from == nullptr) {
            ALOGE("Could not find a source window in %s for %p", __func__, destChannelToken.get());
            return false;
        }

        TouchState& state = it->second;
        const TouchedWindow& touchedWindow = state.windows[0];
        fromToken = touchedWindow.windowHandle->getToken();
        fromToken = from->getToken();
    } // release lock

    return transferTouchFocus(fromToken, destChannelToken);
+4 −1
Original line number Diff line number Diff line
@@ -125,7 +125,7 @@ public:

    bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
                            bool isDragDrop = false) override;
    bool transferTouch(const sp<IBinder>& destChannelToken) override;
    bool transferTouch(const sp<IBinder>& destChannelToken, int32_t displayId) override;

    base::Result<std::unique_ptr<InputChannel>> createInputChannel(
            const std::string& name) override;
@@ -245,6 +245,9 @@ private:
    std::vector<sp<android::gui::WindowInfoHandle>> findTouchedSpyWindowsAtLocked(
            int32_t displayId, int32_t x, int32_t y, bool isStylus) const REQUIRES(mLock);

    sp<android::gui::WindowInfoHandle> findTouchedForegroundWindowLocked(int32_t displayId) const
            REQUIRES(mLock);

    sp<Connection> getConnectionLocked(const sp<IBinder>& inputConnectionToken) const
            REQUIRES(mLock);

+1 −1
Original line number Diff line number Diff line
@@ -161,7 +161,7 @@ public:
     *
     * Return true on success, false if there was no on-going touch.
     */
    virtual bool transferTouch(const sp<IBinder>& destChannelToken) = 0;
    virtual bool transferTouch(const sp<IBinder>& destChannelToken, int32_t displayId) = 0;

    /**
     * Sets focus on the specified window.
+62 −3
Original line number Diff line number Diff line
@@ -2476,6 +2476,63 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) {
    secondWindow->consumeMotionUp();
}

/**
 * When 'transferTouch' API is invoked, dispatcher needs to find the "best" window to take touch
 * from. When we have spy windows, there are several windows to choose from: either spy, or the
 * 'real' (non-spy) window. Always prefer the 'real' window because that's what would be most
 * natural to the user.
 * In this test, we are sending a pointer to both spy window and first window. We then try to
 * transfer touch to the second window. The dispatcher should identify the first window as the
 * one that should lose the gesture, and therefore the action should be to move the gesture from
 * the first window to the second.
 * The main goal here is to test the behaviour of 'transferTouch' API, but it's still valid to test
 * the other API, as well.
 */
TEST_P(TransferTouchFixture, TransferTouch_MultipleWindowsWithSpy) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();

    // Create a couple of windows + a spy window
    sp<FakeWindowHandle> spyWindow =
            new FakeWindowHandle(application, mDispatcher, "Spy", ADISPLAY_ID_DEFAULT);
    spyWindow->setTrustedOverlay(true);
    spyWindow->setSpy(true);
    sp<FakeWindowHandle> firstWindow =
            new FakeWindowHandle(application, mDispatcher, "First", ADISPLAY_ID_DEFAULT);
    sp<FakeWindowHandle> secondWindow =
            new FakeWindowHandle(application, mDispatcher, "Second", ADISPLAY_ID_DEFAULT);

    // Add the windows to the dispatcher
    mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {spyWindow, firstWindow, secondWindow}}});

    // Send down to the first window
    NotifyMotionArgs downMotionArgs =
            generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&downMotionArgs);
    // Only the first window and spy should get the down event
    spyWindow->consumeMotionDown();
    firstWindow->consumeMotionDown();

    // Transfer touch to the second window. Non-spy window should be preferred over the spy window
    // if f === 'transferTouch'.
    TransferFunction f = GetParam();
    const bool success = f(mDispatcher, firstWindow->getToken(), secondWindow->getToken());
    ASSERT_TRUE(success);
    // The first window gets cancel and the second gets down
    firstWindow->consumeMotionCancel();
    secondWindow->consumeMotionDown();

    // Send up event to the second window
    NotifyMotionArgs upMotionArgs =
            generateMotionArgs(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_TOUCHSCREEN,
                               ADISPLAY_ID_DEFAULT);
    mDispatcher->notifyMotion(&upMotionArgs);
    // The first  window gets no events and the second+spy get up
    firstWindow->assertNoEvents();
    spyWindow->consumeMotionUp();
    secondWindow->consumeMotionUp();
}

TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) {
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();

@@ -2545,7 +2602,8 @@ INSTANTIATE_TEST_SUITE_P(TransferFunctionTests, TransferTouchFixture,
                         ::testing::Values(
                                 [&](const std::unique_ptr<InputDispatcher>& dispatcher,
                                     sp<IBinder> /*ignored*/, sp<IBinder> destChannelToken) {
                                     return dispatcher->transferTouch(destChannelToken);
                                     return dispatcher->transferTouch(destChannelToken,
                                                                      ADISPLAY_ID_DEFAULT);
                                 },
                                 [&](const std::unique_ptr<InputDispatcher>& dispatcher,
                                     sp<IBinder> from, sp<IBinder> to) {
@@ -2653,7 +2711,8 @@ TEST_F(InputDispatcherTest, TransferTouch_TwoPointersSplitTouch) {
    secondWindow->consumeMotionDown();

    // Transfer touch focus to the second window
    const bool transferred = mDispatcher->transferTouch(secondWindow->getToken());
    const bool transferred =
            mDispatcher->transferTouch(secondWindow->getToken(), ADISPLAY_ID_DEFAULT);
    // The 'transferTouch' call should not succeed, because there are 2 touched windows
    ASSERT_FALSE(transferred);
    firstWindow->assertNoEvents();
@@ -2777,7 +2836,7 @@ TEST_F(InputDispatcherTest, TransferTouch_CloneSurface) {
    firstWindowInPrimary->consumeMotionDown(SECOND_DISPLAY_ID);

    // Transfer touch focus
    ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken()));
    ASSERT_TRUE(mDispatcher->transferTouch(secondWindowInSecondary->getToken(), SECOND_DISPLAY_ID));

    // The first window gets cancel.
    firstWindowInPrimary->consumeMotionCancel(SECOND_DISPLAY_ID);