Loading services/inputflinger/dispatcher/InputDispatcher.cpp +40 −3 Original line number Diff line number Diff line Loading @@ -5067,8 +5067,19 @@ sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandle( } // Only look through the requested display. for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(*displayId)) { if (windowHandle->getToken() == windowHandleToken) { return findWindowHandleOnDisplay(windowHandleToken, *displayId); } sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnConnectedDisplays( const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const { if (windowHandleToken == nullptr) { return nullptr; } sp<WindowInfoHandle> windowHandle; for (ui::LogicalDisplayId connectedDisplayId : getConnectedDisplays(displayId)) { windowHandle = findWindowHandleOnDisplay(windowHandleToken, connectedDisplayId); if (windowHandle != nullptr) { return windowHandle; } } Loading Loading @@ -5214,6 +5225,29 @@ std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() co return dump; } std::vector<ui::LogicalDisplayId> InputDispatcher::DispatcherWindowInfo::getConnectedDisplays( ui::LogicalDisplayId displayId) const { if (!mTopology.graph.contains(displayId)) { return {displayId}; } std::vector<ui::LogicalDisplayId> connectedDisplays; for (auto it : mTopology.graph) { connectedDisplays.push_back(it.first); } return connectedDisplays; } sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnDisplay( const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const { for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(displayId)) { if (windowHandle->getToken() == windowHandleToken) { return windowHandle; } } return nullptr; } bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion( const sp<android::gui::WindowInfoHandle>& window, const android::inputdispatcher::MotionEntry& motionEntry) const { Loading Loading @@ -5806,7 +5840,10 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB const DeviceId deviceId = *deviceIds.begin(); const sp<WindowInfoHandle> fromWindowHandle = touchedWindow.windowHandle; const sp<WindowInfoHandle> toWindowHandle = mWindowInfos.findWindowHandle(toToken, displayId); // TouchState displayId may not be same as window displayId, we need to lookup for toToken on // all connected displays. const sp<WindowInfoHandle> toWindowHandle = mWindowInfos.findWindowHandleOnConnectedDisplays(toToken, displayId); if (!toWindowHandle) { ALOGW("Cannot transfer touch because the transfer target window was not found."); return std::nullopt; Loading services/inputflinger/dispatcher/InputDispatcher.h +11 −0 Original line number Diff line number Diff line Loading @@ -319,6 +319,11 @@ private: const sp<IBinder>& windowHandleToken, std::optional<ui::LogicalDisplayId> displayId = {}) const; // Lookup for WindowInfoHandle from token and a display-id. Lookup is done for all connected // displays in the topology of the queried display. sp<android::gui::WindowInfoHandle> findWindowHandleOnConnectedDisplays( const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const; bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const; // Returns the touched window at the given location, excluding the ignoreWindow if provided. Loading Loading @@ -349,6 +354,12 @@ private: std::string dumpDisplayAndWindowInfo() const; private: std::vector<ui::LogicalDisplayId> getConnectedDisplays( ui::LogicalDisplayId displayId) const; sp<android::gui::WindowInfoHandle> findWindowHandleOnDisplay( const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const; std::unordered_map<ui::LogicalDisplayId /*displayId*/, std::vector<sp<android::gui::WindowInfoHandle>>> mWindowHandlesByDisplay; Loading services/inputflinger/tests/InputDispatcher_test.cpp +77 −20 Original line number Diff line number Diff line Loading @@ -12414,18 +12414,22 @@ protected: 0}); } void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) { bool consumeButtonPress = false; const PointF location = displayId == ui::LogicalDisplayId::DEFAULT ? PointF(50, 50) : PointF(50, 450); switch (fromSource) { case AINPUT_SOURCE_TOUCHSCREEN: { ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, {50, 50})) injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, displayId, location)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; break; } case AINPUT_SOURCE_STYLUS: { PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(50).y(50); PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(location.x).y(location.y); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, Loading @@ -12448,12 +12452,14 @@ protected: break; } case AINPUT_SOURCE_MOUSE: { PointerBuilder pointer = PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50); PointerBuilder pointer = PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE) .x(location.x) .y(location.y); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) .displayId(displayId) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(pointer) .build())); Loading @@ -12461,6 +12467,7 @@ protected: injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE) .displayId(displayId) .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(pointer) Loading @@ -12474,25 +12481,30 @@ protected: } // Window should receive motion event. mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); sp<FakeWindowHandle>& targetWindow = displayId == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay; targetWindow->consumeMotionDown(displayId); if (consumeButtonPress) { mWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); targetWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); } // Spy window should also receive motion event // Spy window should also receive motion event if event is on the same display. if (displayId == ui::LogicalDisplayId::DEFAULT) { mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); } } // Start performing drag, we will create a drag window and transfer touch to it. // @param sendDown : if true, send a motion down on first window before perform drag and drop. // Returns true on success. bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId dragStartDisplay = ui::LogicalDisplayId::DEFAULT) { if (sendDown) { injectDown(fromSource); injectDown(fromSource, dragStartDisplay); } // The drag window covers the entire display mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", ui::LogicalDisplayId::DEFAULT); mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", dragStartDisplay); mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}}); mDispatcher->onWindowInfosChanged( {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(), Loading @@ -12501,14 +12513,17 @@ protected: 0, 0}); sp<FakeWindowHandle>& targetWindow = dragStartDisplay == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay; // Transfer touch focus to the drag window bool transferred = mDispatcher->transferTouchGesture(mWindow->getToken(), mDragWindow->getToken(), mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(), /*isDragDrop=*/true); if (transferred) { mWindow->consumeMotionCancel(); mDragWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); targetWindow->consumeMotionCancel(dragStartDisplay); mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } return transferred; } Loading Loading @@ -15292,10 +15307,10 @@ TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseGesture) { mWindow->consumeMotionUp(SECOND_DISPLAY_ID); } TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDrop) { TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromPrimaryDisplay) { SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); startDrag(true, AINPUT_SOURCE_MOUSE); EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE)); // Move on window. mDispatcher->notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) Loading Loading @@ -15346,4 +15361,46 @@ TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDrop) { mWindowOnSecondDisplay->assertNoEvents(); } TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromNonPrimaryDisplay) { SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID)); // Move on window. mDispatcher->notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) .displayId(SECOND_DISPLAY_ID) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) .build()); mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50); // Move to window on the primary display mDispatcher->notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) .displayId(DISPLAY_ID) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) .build()); mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->assertNoEvents(); mWindowOnSecondDisplay->consumeDragEvent(true, 50, 50); // drop on the primary display mDispatcher->notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) .displayId(DISPLAY_ID) .buttonState(0) .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) .build()); mDragWindow->consumeMotionUp(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken()); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); mWindowOnSecondDisplay->assertNoEvents(); } } // namespace android::inputdispatcher Loading
services/inputflinger/dispatcher/InputDispatcher.cpp +40 −3 Original line number Diff line number Diff line Loading @@ -5067,8 +5067,19 @@ sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandle( } // Only look through the requested display. for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(*displayId)) { if (windowHandle->getToken() == windowHandleToken) { return findWindowHandleOnDisplay(windowHandleToken, *displayId); } sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnConnectedDisplays( const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const { if (windowHandleToken == nullptr) { return nullptr; } sp<WindowInfoHandle> windowHandle; for (ui::LogicalDisplayId connectedDisplayId : getConnectedDisplays(displayId)) { windowHandle = findWindowHandleOnDisplay(windowHandleToken, connectedDisplayId); if (windowHandle != nullptr) { return windowHandle; } } Loading Loading @@ -5214,6 +5225,29 @@ std::string InputDispatcher::DispatcherWindowInfo::dumpDisplayAndWindowInfo() co return dump; } std::vector<ui::LogicalDisplayId> InputDispatcher::DispatcherWindowInfo::getConnectedDisplays( ui::LogicalDisplayId displayId) const { if (!mTopology.graph.contains(displayId)) { return {displayId}; } std::vector<ui::LogicalDisplayId> connectedDisplays; for (auto it : mTopology.graph) { connectedDisplays.push_back(it.first); } return connectedDisplays; } sp<WindowInfoHandle> InputDispatcher::DispatcherWindowInfo::findWindowHandleOnDisplay( const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const { for (const sp<WindowInfoHandle>& windowHandle : getWindowHandlesForDisplay(displayId)) { if (windowHandle->getToken() == windowHandleToken) { return windowHandle; } } return nullptr; } bool InputDispatcher::DispatcherTouchState::canWindowReceiveMotion( const sp<android::gui::WindowInfoHandle>& window, const android::inputdispatcher::MotionEntry& motionEntry) const { Loading Loading @@ -5806,7 +5840,10 @@ InputDispatcher::DispatcherTouchState::transferTouchGesture(const sp<android::IB const DeviceId deviceId = *deviceIds.begin(); const sp<WindowInfoHandle> fromWindowHandle = touchedWindow.windowHandle; const sp<WindowInfoHandle> toWindowHandle = mWindowInfos.findWindowHandle(toToken, displayId); // TouchState displayId may not be same as window displayId, we need to lookup for toToken on // all connected displays. const sp<WindowInfoHandle> toWindowHandle = mWindowInfos.findWindowHandleOnConnectedDisplays(toToken, displayId); if (!toWindowHandle) { ALOGW("Cannot transfer touch because the transfer target window was not found."); return std::nullopt; Loading
services/inputflinger/dispatcher/InputDispatcher.h +11 −0 Original line number Diff line number Diff line Loading @@ -319,6 +319,11 @@ private: const sp<IBinder>& windowHandleToken, std::optional<ui::LogicalDisplayId> displayId = {}) const; // Lookup for WindowInfoHandle from token and a display-id. Lookup is done for all connected // displays in the topology of the queried display. sp<android::gui::WindowInfoHandle> findWindowHandleOnConnectedDisplays( const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const; bool isWindowPresent(const sp<android::gui::WindowInfoHandle>& windowHandle) const; // Returns the touched window at the given location, excluding the ignoreWindow if provided. Loading Loading @@ -349,6 +354,12 @@ private: std::string dumpDisplayAndWindowInfo() const; private: std::vector<ui::LogicalDisplayId> getConnectedDisplays( ui::LogicalDisplayId displayId) const; sp<android::gui::WindowInfoHandle> findWindowHandleOnDisplay( const sp<IBinder>& windowHandleToken, ui::LogicalDisplayId displayId) const; std::unordered_map<ui::LogicalDisplayId /*displayId*/, std::vector<sp<android::gui::WindowInfoHandle>>> mWindowHandlesByDisplay; Loading
services/inputflinger/tests/InputDispatcher_test.cpp +77 −20 Original line number Diff line number Diff line Loading @@ -12414,18 +12414,22 @@ protected: 0}); } void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { void injectDown(int fromSource = AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId displayId = ui::LogicalDisplayId::DEFAULT) { bool consumeButtonPress = false; const PointF location = displayId == ui::LogicalDisplayId::DEFAULT ? PointF(50, 50) : PointF(50, 450); switch (fromSource) { case AINPUT_SOURCE_TOUCHSCREEN: { ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId::DEFAULT, {50, 50})) injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, displayId, location)) << "Inject motion event should return InputEventInjectionResult::SUCCEEDED"; break; } case AINPUT_SOURCE_STYLUS: { PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(50).y(50); PointerBuilder pointer = PointerBuilder(0, ToolType::STYLUS).x(location.x).y(location.y); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, Loading @@ -12448,12 +12452,14 @@ protected: break; } case AINPUT_SOURCE_MOUSE: { PointerBuilder pointer = PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50); PointerBuilder pointer = PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE) .x(location.x) .y(location.y); ASSERT_EQ(InputEventInjectionResult::SUCCEEDED, injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_MOUSE) .displayId(displayId) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(pointer) .build())); Loading @@ -12461,6 +12467,7 @@ protected: injectMotionEvent(*mDispatcher, MotionEventBuilder(AMOTION_EVENT_ACTION_BUTTON_PRESS, AINPUT_SOURCE_MOUSE) .displayId(displayId) .actionButton(AMOTION_EVENT_BUTTON_PRIMARY) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(pointer) Loading @@ -12474,25 +12481,30 @@ protected: } // Window should receive motion event. mWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); sp<FakeWindowHandle>& targetWindow = displayId == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay; targetWindow->consumeMotionDown(displayId); if (consumeButtonPress) { mWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); targetWindow->consumeMotionEvent(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_PRESS)); } // Spy window should also receive motion event // Spy window should also receive motion event if event is on the same display. if (displayId == ui::LogicalDisplayId::DEFAULT) { mSpyWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT); } } // Start performing drag, we will create a drag window and transfer touch to it. // @param sendDown : if true, send a motion down on first window before perform drag and drop. // Returns true on success. bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN) { bool startDrag(bool sendDown = true, int fromSource = AINPUT_SOURCE_TOUCHSCREEN, ui::LogicalDisplayId dragStartDisplay = ui::LogicalDisplayId::DEFAULT) { if (sendDown) { injectDown(fromSource); injectDown(fromSource, dragStartDisplay); } // The drag window covers the entire display mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", ui::LogicalDisplayId::DEFAULT); mDragWindow = sp<FakeWindowHandle>::make(mApp, mDispatcher, "DragWindow", dragStartDisplay); mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}}); mDispatcher->onWindowInfosChanged( {{*mDragWindow->getInfo(), *mSpyWindow->getInfo(), *mWindow->getInfo(), Loading @@ -12501,14 +12513,17 @@ protected: 0, 0}); sp<FakeWindowHandle>& targetWindow = dragStartDisplay == ui::LogicalDisplayId::DEFAULT ? mWindow : mWindowOnSecondDisplay; // Transfer touch focus to the drag window bool transferred = mDispatcher->transferTouchGesture(mWindow->getToken(), mDragWindow->getToken(), mDispatcher->transferTouchGesture(targetWindow->getToken(), mDragWindow->getToken(), /*isDragDrop=*/true); if (transferred) { mWindow->consumeMotionCancel(); mDragWindow->consumeMotionDown(ui::LogicalDisplayId::DEFAULT, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); targetWindow->consumeMotionCancel(dragStartDisplay); mDragWindow->consumeMotionDown(dragStartDisplay, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); } return transferred; } Loading Loading @@ -15292,10 +15307,10 @@ TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseGesture) { mWindow->consumeMotionUp(SECOND_DISPLAY_ID); } TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDrop) { TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromPrimaryDisplay) { SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); startDrag(true, AINPUT_SOURCE_MOUSE); EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE)); // Move on window. mDispatcher->notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) Loading Loading @@ -15346,4 +15361,46 @@ TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDrop) { mWindowOnSecondDisplay->assertNoEvents(); } TEST_F(InputDispatcherConnectedDisplayTest, MultiDisplayMouseDragAndDropFromNonPrimaryDisplay) { SCOPED_FLAG_OVERRIDE(connected_displays_cursor, true); EXPECT_TRUE(startDrag(true, AINPUT_SOURCE_MOUSE, SECOND_DISPLAY_ID)); // Move on window. mDispatcher->notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) .displayId(SECOND_DISPLAY_ID) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) .build()); mDragWindow->consumeMotionMove(SECOND_DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); mWindowOnSecondDisplay->consumeDragEvent(false, 50, 50); // Move to window on the primary display mDispatcher->notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_MOUSE) .displayId(DISPLAY_ID) .buttonState(AMOTION_EVENT_BUTTON_PRIMARY) .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) .build()); mDragWindow->consumeMotionMove(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mWindow->consumeDragEvent(false, 50, 50); mSecondWindow->assertNoEvents(); mWindowOnSecondDisplay->consumeDragEvent(true, 50, 50); // drop on the primary display mDispatcher->notifyMotion( MotionArgsBuilder(AMOTION_EVENT_ACTION_UP, AINPUT_SOURCE_MOUSE) .displayId(DISPLAY_ID) .buttonState(0) .pointer(PointerBuilder(MOUSE_POINTER_ID, ToolType::MOUSE).x(50).y(50)) .build()); mDragWindow->consumeMotionUp(DISPLAY_ID, AMOTION_EVENT_FLAG_NO_FOCUS_CHANGE); mFakePolicy->assertDropTargetEquals(*mDispatcher, mWindow->getToken()); mWindow->assertNoEvents(); mSecondWindow->assertNoEvents(); mWindowOnSecondDisplay->assertNoEvents(); } } // namespace android::inputdispatcher