Loading services/inputflinger/dispatcher/InputDispatcher.cpp +39 −13 Original line number Diff line number Diff line Loading @@ -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); Loading services/inputflinger/dispatcher/InputDispatcher.h +4 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading services/inputflinger/dispatcher/include/InputDispatcherInterface.h +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading services/inputflinger/tests/InputDispatcher_test.cpp +62 −3 Original line number Diff line number Diff line Loading @@ -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>(); Loading Loading @@ -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) { Loading Loading @@ -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(); Loading Loading @@ -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); Loading Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +39 −13 Original line number Diff line number Diff line Loading @@ -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); Loading
services/inputflinger/dispatcher/InputDispatcher.h +4 −1 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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); Loading
services/inputflinger/dispatcher/include/InputDispatcherInterface.h +1 −1 Original line number Diff line number Diff line Loading @@ -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. Loading
services/inputflinger/tests/InputDispatcher_test.cpp +62 −3 Original line number Diff line number Diff line Loading @@ -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>(); Loading Loading @@ -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) { Loading Loading @@ -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(); Loading Loading @@ -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); Loading