diff options
author | TreeHugger Robot <treehugger-gerrit@google.com> | 2023-01-10 10:31:38 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2023-01-10 10:31:38 +0000 |
commit | b4ae6eced199f72cda3c42fd09fe72629c05a35f (patch) | |
tree | d8a7ec654f90dea2c2ef01ae1b14c4ffb32737bc | |
parent | b82529652716ee9c339260f2454da60025a74e42 (diff) | |
parent | ba703c3cd22969200458d7c986a3f1741109762d (diff) | |
download | native-b4ae6eced199f72cda3c42fd09fe72629c05a35f.tar.gz |
Merge "Fix wallpaper window multi-touch" into tm-qpr-dev
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 184 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.h | 20 | ||||
-rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 201 |
3 files changed, 297 insertions, 108 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index 5c47be9be0..4ad9f42cdb 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -2196,8 +2196,31 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( // Update the temporary touch state. BitSet32 pointerIds; pointerIds.markBit(entry.pointerProperties[pointerIndex].id); - tempTouchState.addOrUpdateWindow(windowHandle, targetFlags, pointerIds); + + // 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 (maskedAction == AMOTION_EVENT_ACTION_DOWN || + maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN) { + if ((targetFlags & InputTarget::FLAG_FOREGROUND) && + windowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER)) { + sp<WindowInfoHandle> wallpaper = findWallpaperWindowBelow(windowHandle); + if (wallpaper != nullptr) { + int32_t wallpaperFlags = InputTarget::FLAG_WINDOW_IS_OBSCURED | + InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED | + InputTarget::FLAG_DISPATCH_AS_IS; + if (isSplit) { + wallpaperFlags |= InputTarget::FLAG_SPLIT; + } + tempTouchState.addOrUpdateWindow(wallpaper, wallpaperFlags, pointerIds); + } + } + } } } else { /* Case 2: Pointer move, up, cancel or non-splittable pointer down. */ @@ -2274,6 +2297,10 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( BitSet32 pointerIds; pointerIds.markBit(entry.pointerProperties[0].id); tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds); + + // Check if the wallpaper window should deliver the corresponding event. + slipWallpaperTouch(targetFlags, oldTouchedWindowHandle, newTouchedWindowHandle, + tempTouchState, pointerIds); } } @@ -2379,39 +2406,6 @@ InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( } } - // 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)) { - BitSet32 pointerIds; - pointerIds.markBit(entry.pointerProperties[0].id); - tempTouchState - .addOrUpdateWindow(windowHandle, - InputTarget::FLAG_WINDOW_IS_OBSCURED | - InputTarget:: - FLAG_WINDOW_IS_PARTIALLY_OBSCURED | - InputTarget::FLAG_DISPATCH_AS_IS, - pointerIds); - } - } - } - } - // Success! Output targets. injectionResult = InputEventInjectionResult::SUCCEEDED; @@ -3685,7 +3679,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked( } void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( - const sp<Connection>& connection) { + const sp<Connection>& connection, int32_t targetFlags) { if (connection->status == Connection::Status::BROKEN) { return; } @@ -3713,7 +3707,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( target.globalScaleFactor = windowInfo->globalScaleFactor; } target.inputChannel = connection->inputChannel; - target.flags = InputTarget::FLAG_DISPATCH_AS_IS; + target.flags = targetFlags; for (std::unique_ptr<EventEntry>& downEventEntry : downEvents) { switch (downEventEntry->type) { @@ -3744,6 +3738,16 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked( startDispatchCycleLocked(currentTime, connection); } +void InputDispatcher::synthesizeCancelationEventsForWindowLocked( + const sp<WindowInfoHandle>& windowHandle, const CancelationOptions& options) { + if (windowHandle != nullptr) { + sp<Connection> wallpaperConnection = getConnectionLocked(windowHandle->getToken()); + if (wallpaperConnection != nullptr) { + synthesizeCancelationEventsForConnectionLocked(wallpaperConnection, options); + } + } +} + std::unique_ptr<MotionEntry> InputDispatcher::splitMotionEvent( const MotionEntry& originalMotionEntry, BitSet32 pointerIds) { ALOG_ASSERT(pointerIds.value != 0); @@ -4762,14 +4766,7 @@ void InputDispatcher::setInputWindowsLocked( 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); - } - } + synthesizeCancelationEventsForWindowLocked(wallpaper, options); } } state.windows.erase(state.windows.begin() + i); @@ -5082,6 +5079,7 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< // Erase old window. int32_t oldTargetFlags = touchedWindow->targetFlags; BitSet32 pointerIds = touchedWindow->pointerIds; + sp<WindowInfoHandle> fromWindowHandle = touchedWindow->windowHandle; state->removeWindowByToken(fromToken); // Add new window. @@ -5113,7 +5111,10 @@ bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp< options(CancelationOptions::CANCEL_POINTER_EVENTS, "transferring touch focus from this window to another window"); synthesizeCancelationEventsForConnectionLocked(fromConnection, options); - synthesizePointerDownEventsForConnectionLocked(toConnection); + synthesizePointerDownEventsForConnectionLocked(toConnection, newTargetFlags); + // Check if the wallpaper window should deliver the corresponding event. + transferWallpaperTouch(oldTargetFlags, newTargetFlags, fromWindowHandle, toWindowHandle, + *state, pointerIds); } if (DEBUG_FOCUS) { @@ -6398,4 +6399,97 @@ void InputDispatcher::setMonitorDispatchingTimeoutForTest(std::chrono::nanosecon mMonitorDispatchingTimeout = timeout; } +void InputDispatcher::slipWallpaperTouch(int32_t targetFlags, + const sp<WindowInfoHandle>& oldWindowHandle, + const sp<WindowInfoHandle>& newWindowHandle, + TouchState& state, const BitSet32& pointerIds) { + const bool oldHasWallpaper = oldWindowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); + const bool newHasWallpaper = (targetFlags & InputTarget::FLAG_FOREGROUND) && + newWindowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); + const sp<WindowInfoHandle> oldWallpaper = + oldHasWallpaper ? state.getWallpaperWindow() : nullptr; + const sp<WindowInfoHandle> newWallpaper = + newHasWallpaper ? findWallpaperWindowBelow(newWindowHandle) : nullptr; + if (oldWallpaper == newWallpaper) { + return; + } + + if (oldWallpaper != nullptr) { + state.addOrUpdateWindow(oldWallpaper, InputTarget::FLAG_DISPATCH_AS_SLIPPERY_EXIT, + BitSet32(0)); + } + + if (newWallpaper != nullptr) { + state.addOrUpdateWindow(newWallpaper, + InputTarget::FLAG_DISPATCH_AS_SLIPPERY_ENTER | + InputTarget::FLAG_WINDOW_IS_OBSCURED | + InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED, + pointerIds); + } +} + +void InputDispatcher::transferWallpaperTouch(int32_t oldTargetFlags, int32_t newTargetFlags, + const sp<WindowInfoHandle> fromWindowHandle, + const sp<WindowInfoHandle> toWindowHandle, + TouchState& state, const BitSet32& pointerIds) { + const bool oldHasWallpaper = (oldTargetFlags & InputTarget::FLAG_FOREGROUND) && + fromWindowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); + const bool newHasWallpaper = (newTargetFlags & InputTarget::FLAG_FOREGROUND) && + toWindowHandle->getInfo()->inputConfig.test( + gui::WindowInfo::InputConfig::DUPLICATE_TOUCH_TO_WALLPAPER); + + const sp<WindowInfoHandle> oldWallpaper = + oldHasWallpaper ? state.getWallpaperWindow() : nullptr; + const sp<WindowInfoHandle> newWallpaper = + newHasWallpaper ? findWallpaperWindowBelow(toWindowHandle) : nullptr; + if (oldWallpaper == newWallpaper) { + return; + } + + if (oldWallpaper != nullptr) { + CancelationOptions options(CancelationOptions::Mode::CANCEL_POINTER_EVENTS, + "transferring touch focus to another window"); + state.removeWindowByToken(oldWallpaper->getToken()); + synthesizeCancelationEventsForWindowLocked(oldWallpaper, options); + } + + if (newWallpaper != nullptr) { + int32_t wallpaperFlags = + oldTargetFlags & (InputTarget::FLAG_SPLIT | InputTarget::FLAG_DISPATCH_AS_IS); + wallpaperFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED | + InputTarget::FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + state.addOrUpdateWindow(newWallpaper, wallpaperFlags, pointerIds); + sp<Connection> wallpaperConnection = getConnectionLocked(newWallpaper->getToken()); + if (wallpaperConnection != nullptr) { + sp<Connection> toConnection = getConnectionLocked(toWindowHandle->getToken()); + toConnection->inputState.mergePointerStateTo(wallpaperConnection->inputState); + synthesizePointerDownEventsForConnectionLocked(wallpaperConnection, wallpaperFlags); + } + } +} + +sp<WindowInfoHandle> InputDispatcher::findWallpaperWindowBelow( + const sp<WindowInfoHandle>& windowHandle) const { + const std::vector<sp<WindowInfoHandle>>& windowHandles = + getWindowHandlesLocked(windowHandle->getInfo()->displayId); + bool foundWindow = false; + for (const sp<WindowInfoHandle>& otherHandle : windowHandles) { + if (!foundWindow && otherHandle != windowHandle) { + continue; + } + if (windowHandle == otherHandle) { + foundWindow = true; + continue; + } + + if (otherHandle->getInfo()->inputConfig.test(WindowInfo::InputConfig::IS_WALLPAPER)) { + return otherHandle; + } + } + return nullptr; +} + } // namespace android::inputdispatcher diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h index ed89ed0b0f..0df589480d 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.h +++ b/services/inputflinger/dispatcher/InputDispatcher.h @@ -621,8 +621,12 @@ private: const CancelationOptions& options) REQUIRES(mLock); - void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection) - REQUIRES(mLock); + void synthesizePointerDownEventsForConnectionLocked(const sp<Connection>& connection, + int32_t targetFlags) REQUIRES(mLock); + + void synthesizeCancelationEventsForWindowLocked( + const sp<android::gui::WindowInfoHandle>& windowHandle, + const CancelationOptions& options) REQUIRES(mLock); // Splitting motion events across windows. std::unique_ptr<MotionEntry> splitMotionEvent(const MotionEntry& originalMotionEntry, @@ -684,6 +688,18 @@ private: bool recentWindowsAreOwnedByLocked(int32_t pid, int32_t uid) REQUIRES(mLock); sp<InputReporterInterface> mReporter; + + void slipWallpaperTouch(int32_t targetFlags, + const sp<android::gui::WindowInfoHandle>& oldWindowHandle, + const sp<android::gui::WindowInfoHandle>& newWindowHandle, + TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock); + void transferWallpaperTouch(int32_t oldTargetFlags, int32_t newTargetFlags, + const sp<android::gui::WindowInfoHandle> fromWindowHandle, + const sp<android::gui::WindowInfoHandle> toWindowHandle, + TouchState& state, const BitSet32& pointerIds) REQUIRES(mLock); + + sp<android::gui::WindowInfoHandle> findWallpaperWindowBelow( + const sp<android::gui::WindowInfoHandle>& windowHandle) const REQUIRES(mLock); }; } // namespace android::inputdispatcher diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp index 58617f7383..ca0ef2a43d 100644 --- a/services/inputflinger/tests/InputDispatcher_test.cpp +++ b/services/inputflinger/tests/InputDispatcher_test.cpp @@ -58,6 +58,8 @@ static constexpr int32_t POINTER_1_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); static constexpr int32_t POINTER_2_DOWN = AMOTION_EVENT_ACTION_POINTER_DOWN | (2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); +static constexpr int32_t POINTER_0_UP = + AMOTION_EVENT_ACTION_POINTER_UP | (0 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); static constexpr int32_t POINTER_1_UP = AMOTION_EVENT_ACTION_POINTER_UP | (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT); @@ -73,6 +75,9 @@ static constexpr int32_t MONITOR_PID = 2001; static constexpr std::chrono::duration STALE_EVENT_TIMEOUT = 1000ms; +static constexpr int expectedWallpaperFlags = + AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; + struct PointF { float x; float y; @@ -1670,8 +1675,6 @@ TEST_F(InputDispatcherTest, WhenForegroundWindowDisappears_WallpaperTouchIsCance sp<FakeWindowHandle> wallpaperWindow = new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -1714,8 +1717,6 @@ TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) { sp<FakeWindowHandle> wallpaperWindow = new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}}); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -1745,24 +1746,27 @@ TEST_F(InputDispatcherTest, WhenWallpaperDisappears_NoCrash) { foregroundWindow->consumeMotionCancel(); } +class ShouldSplitTouchFixture : public InputDispatcherTest, + public ::testing::WithParamInterface<bool> {}; +INSTANTIATE_TEST_SUITE_P(InputDispatcherTest, ShouldSplitTouchFixture, + ::testing::Values(true, false)); /** * A single window that receives touch (on top), and a wallpaper window underneath it. * The top window gets a multitouch gesture. * Ensure that wallpaper gets the same gesture. */ -TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { +TEST_P(ShouldSplitTouchFixture, WallpaperWindowReceivesMultiTouch) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - new FakeWindowHandle(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); - window->setDupTouchToWallpaper(true); + sp<FakeWindowHandle> foregroundWindow = + new FakeWindowHandle(application, mDispatcher, "Foreground", ADISPLAY_ID_DEFAULT); + foregroundWindow->setDupTouchToWallpaper(true); + foregroundWindow->setPreventSplitting(GetParam()); sp<FakeWindowHandle> wallpaperWindow = new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {foregroundWindow, wallpaperWindow}}}); // Touch down on top window ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, @@ -1771,7 +1775,7 @@ TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; // Both top window and its wallpaper should receive the touch down - window->consumeMotionDown(); + foregroundWindow->consumeMotionDown(); wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); // Second finger down on the top window @@ -1790,11 +1794,34 @@ TEST_F(InputDispatcherTest, WallpaperWindow_ReceivesMultiTouch) { InputEventInjectionSync::WAIT_FOR_RESULT)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionPointerDown(1 /* pointerIndex */); + foregroundWindow->consumeMotionPointerDown(1 /* pointerIndex */); wallpaperWindow->consumeMotionPointerDown(1 /* pointerIndex */, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); - window->assertNoEvents(); - wallpaperWindow->assertNoEvents(); + + const MotionEvent secondFingerUpEvent = + MotionEventBuilder(POINTER_0_UP, AINPUT_SOURCE_TOUCHSCREEN) + .displayId(ADISPLAY_ID_DEFAULT) + .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) + .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(100) + .y(100)) + .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER) + .x(150) + .y(150)) + .build(); + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT, + InputEventInjectionSync::WAIT_FOR_RESULT)) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + foregroundWindow->consumeMotionPointerUp(0); + wallpaperWindow->consumeMotionPointerUp(0, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, + injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, + {100, 100})) + << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; + foregroundWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT); + wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); } /** @@ -1821,8 +1848,6 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setFrame(Rect(0, 0, 400, 200)); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; mDispatcher->setInputWindows( {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}}); @@ -1887,62 +1912,49 @@ TEST_F(InputDispatcherTest, TwoWindows_SplitWallpaperTouch) { wallpaperWindow->assertNoEvents(); } -TEST_F(InputDispatcherTest, WallpaperWindowReceivesMultiTouch) { +/** + * Two windows: a window on the left with dup touch to wallpaper and window on the right without it. + * The touch slips to the right window. so left window and wallpaper should receive ACTION_CANCEL + * The right window should receive ACTION_DOWN. + */ +TEST_F(InputDispatcherTest, WallpaperWindowWhenSlippery) { std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); - sp<FakeWindowHandle> window = - sp<FakeWindowHandle>::make(application, mDispatcher, "Top", ADISPLAY_ID_DEFAULT); - window->setDupTouchToWallpaper(true); + sp<FakeWindowHandle> leftWindow = + new FakeWindowHandle(application, mDispatcher, "Left", ADISPLAY_ID_DEFAULT); + leftWindow->setFrame(Rect(0, 0, 200, 200)); + leftWindow->setDupTouchToWallpaper(true); + leftWindow->setSlippery(true); + + sp<FakeWindowHandle> rightWindow = + new FakeWindowHandle(application, mDispatcher, "Right", ADISPLAY_ID_DEFAULT); + rightWindow->setFrame(Rect(200, 0, 400, 200)); sp<FakeWindowHandle> wallpaperWindow = - sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); wallpaperWindow->setIsWallpaper(true); - constexpr int expectedWallpaperFlags = - AMOTION_EVENT_FLAG_WINDOW_IS_OBSCURED | AMOTION_EVENT_FLAG_WINDOW_IS_PARTIALLY_OBSCURED; - wallpaperWindow->setPreventSplitting(true); - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {window, wallpaperWindow}}}); + mDispatcher->setInputWindows( + {{ADISPLAY_ID_DEFAULT, {leftWindow, rightWindow, wallpaperWindow}}}); + // Touch down on left window ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, - {50, 50})) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionDown(ADISPLAY_ID_DEFAULT); - wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); - - const MotionEvent secondFingerDownEvent = - MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN) - .displayId(ADISPLAY_ID_DEFAULT) - .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) - .build(); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT, - InputEventInjectionSync::WAIT_FOR_RESULT)) + {100, 100})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionPointerDown(1); - wallpaperWindow->consumeMotionPointerDown(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + // Both foreground window and its wallpaper should receive the touch down + leftWindow->consumeMotionDown(); + wallpaperWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); - const MotionEvent secondFingerUpEvent = - MotionEventBuilder(POINTER_1_UP, AINPUT_SOURCE_TOUCHSCREEN) - .displayId(ADISPLAY_ID_DEFAULT) - .eventTime(systemTime(SYSTEM_TIME_MONOTONIC)) - .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50)) - .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(10).y(10)) - .build(); + // Move to right window, the left window should receive cancel. ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionEvent(mDispatcher, secondFingerUpEvent, INJECT_EVENT_TIMEOUT, - InputEventInjectionSync::WAIT_FOR_RESULT)) + injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN, + ADISPLAY_ID_DEFAULT, {201, 100})) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionPointerUp(1); - wallpaperWindow->consumeMotionPointerUp(1, ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); - ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, - injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50})) - << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; - window->consumeMotionUp(ADISPLAY_ID_DEFAULT); - wallpaperWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + leftWindow->consumeMotionCancel(); + rightWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT); + wallpaperWindow->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); } /** @@ -2567,20 +2579,26 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { // Create a couple of windows sp<FakeWindowHandle> firstWindow = new FakeWindowHandle(application, mDispatcher, "First Window", ADISPLAY_ID_DEFAULT); + firstWindow->setDupTouchToWallpaper(true); + sp<FakeWindowHandle> secondWindow = new FakeWindowHandle(application, mDispatcher, "Second Window", ADISPLAY_ID_DEFAULT); - + sp<FakeWindowHandle> wallpaper = + new FakeWindowHandle(application, mDispatcher, "Wallpaper", ADISPLAY_ID_DEFAULT); + wallpaper->setIsWallpaper(true); // Add the windows to the dispatcher - mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow}}}); + mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {firstWindow, secondWindow, wallpaper}}}); // 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 should get the down event firstWindow->consumeMotionDown(); secondWindow->assertNoEvents(); + wallpaper->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); // Transfer touch to the second window TransferFunction f = GetParam(); @@ -2589,6 +2607,7 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { // The first window gets cancel and the second gets down firstWindow->consumeMotionCancel(); secondWindow->consumeMotionDown(); + wallpaper->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); // Send up event to the second window NotifyMotionArgs upMotionArgs = @@ -2598,6 +2617,7 @@ TEST_P(TransferTouchFixture, TransferTouch_OnePointer) { // The first window gets no events and the second gets up firstWindow->assertNoEvents(); secondWindow->consumeMotionUp(); + wallpaper->assertNoEvents(); } /** @@ -2719,6 +2739,65 @@ TEST_P(TransferTouchFixture, TransferTouch_TwoPointersNonSplitTouch) { secondWindow->consumeMotionUp(); } +TEST_P(TransferTouchFixture, TransferTouch_MultipleWallpapers) { + std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); + + // Create a couple of windows + sp<FakeWindowHandle> firstWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "First Window", + ADISPLAY_ID_DEFAULT); + firstWindow->setDupTouchToWallpaper(true); + sp<FakeWindowHandle> secondWindow = + sp<FakeWindowHandle>::make(application, mDispatcher, "Second Window", + ADISPLAY_ID_DEFAULT); + secondWindow->setDupTouchToWallpaper(true); + + sp<FakeWindowHandle> wallpaper1 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper1", ADISPLAY_ID_DEFAULT); + wallpaper1->setIsWallpaper(true); + + sp<FakeWindowHandle> wallpaper2 = + sp<FakeWindowHandle>::make(application, mDispatcher, "Wallpaper2", ADISPLAY_ID_DEFAULT); + wallpaper2->setIsWallpaper(true); + // Add the windows to the dispatcher + mDispatcher->setInputWindows( + {{ADISPLAY_ID_DEFAULT, {firstWindow, wallpaper1, secondWindow, wallpaper2}}}); + + // 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 should get the down event + firstWindow->consumeMotionDown(); + secondWindow->assertNoEvents(); + wallpaper1->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + wallpaper2->assertNoEvents(); + + // Transfer touch focus to the second window + TransferFunction f = GetParam(); + 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(); + wallpaper1->consumeMotionCancel(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + wallpaper2->consumeMotionDown(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); + + // 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 gets up + firstWindow->assertNoEvents(); + secondWindow->consumeMotionUp(); + wallpaper1->assertNoEvents(); + wallpaper2->consumeMotionUp(ADISPLAY_ID_DEFAULT, expectedWallpaperFlags); +} + // For the cases of single pointer touch and two pointers non-split touch, the api's // 'transferTouch' and 'transferTouchFocus' are equivalent in behaviour. They only differ // for the case where there are multiple pointers split across several windows. |