summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Linnan Li <lilinnan@xiaomi.corp-partner.google.com> 2023-07-14 14:36:22 +0800
committer Prabir Pradhan <prabirmsp@google.com> 2023-10-05 19:30:11 +0000
commit5af92f97f44ff2b96fd6d78e0843f2f133627f88 (patch)
treebd139b79a893066a738fb3276354afc06006bd5d
parent1c29a09b42745d229a77773abe80c9d46137c0e3 (diff)
Cancel drag and drop when pointers are canceled for the drag window
When a cancel event is synthesized for any reason for the drag window when drag and drop is active, we should cancel the ongoing drag and drop operation. Otherwise, the system may end up in an unexpected state resulting in unwanted behavior. Bug: 291181957 Test: atest inputflinger_tests Change-Id: Ic5e328f0e445e3271cb1797725c4c00589a6b5e1 Signed-off-by: Linnan Li <lilinnan@xiaomi.corp-partner.google.com>
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp10
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp70
2 files changed, 80 insertions, 0 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index be579329ad..1589278a68 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3921,6 +3921,16 @@ void InputDispatcher::synthesizeCancelationEventsForInputChannelLocked(
void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const std::shared_ptr<Connection>& connection, const CancelationOptions& options) {
+ if ((options.mode == CancelationOptions::Mode::CANCEL_POINTER_EVENTS ||
+ options.mode == CancelationOptions::Mode::CANCEL_ALL_EVENTS) &&
+ mDragState && mDragState->dragWindow->getToken() == connection->inputChannel->getToken()) {
+ LOG(INFO) << __func__
+ << ": Canceling drag and drop because the pointers for the drag window are being "
+ "canceled.";
+ sendDropWindowCommandLocked(nullptr, /*x=*/0, /*y=*/0);
+ mDragState.reset();
+ }
+
if (connection->status == Connection::Status::BROKEN) {
return;
}
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index d532d50051..395ce03826 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -9367,6 +9367,76 @@ TEST_F(InputDispatcherDragTests, MouseDragAndDrop) {
mSecondWindow->assertNoEvents();
}
+/**
+ * Start drag and drop with a pointer whose id is not 0, cancel the current touch, and ensure drag
+ * and drop is also canceled. Then inject a simple gesture, and ensure dispatcher does not crash.
+ */
+TEST_F(InputDispatcherDragTests, DragAndDropFinishedWhenCancelCurrentTouch) {
+ // Down on second window
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionDown());
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionDown());
+
+ // Down on first window
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(150).y(50))
+ .pointer(PointerBuilder(/*id=*/1, ToolType::FINGER).x(50).y(50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+ ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionMove());
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionPointerDown(1));
+
+ // Start drag on first window
+ ASSERT_TRUE(startDrag(/*sendDown=*/false, AINPUT_SOURCE_TOUCHSCREEN));
+
+ // Trigger cancel
+ mDispatcher->cancelCurrentTouch();
+ ASSERT_NO_FATAL_FAILURE(mSecondWindow->consumeMotionCancel());
+ ASSERT_NO_FATAL_FAILURE(mDragWindow->consumeMotionCancel());
+ ASSERT_NO_FATAL_FAILURE(mSpyWindow->consumeMotionCancel());
+
+ ASSERT_TRUE(mDispatcher->waitForIdle());
+ // The D&D finished with nullptr
+ mFakePolicy->assertDropTargetEquals(*mDispatcher, nullptr);
+
+ // Remove drag window
+ mDispatcher->onWindowInfosChanged({{*mWindow->getInfo(), *mSecondWindow->getInfo()}, {}, 0, 0});
+
+ // Inject a simple gesture, ensure dispatcher not crashed
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ PointF{50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionDown());
+
+ const MotionEvent moveEvent =
+ MotionEventBuilder(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .displayId(ADISPLAY_ID_DEFAULT)
+ .pointer(PointerBuilder(/*id=*/0, ToolType::FINGER).x(50).y(50))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(*mDispatcher, moveEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionMove());
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(*mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ ASSERT_NO_FATAL_FAILURE(mWindow->consumeMotionUp());
+}
+
class InputDispatcherDropInputFeatureTest : public InputDispatcherTest {};
TEST_F(InputDispatcherDropInputFeatureTest, WindowDropsInput) {