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-09 19:49:58 +0000
commita4659fc08dc0d5de7077a9ffc501634c3b249bda (patch)
treec539e6a82a420161463c083a0c487c12a3a0b78d
parent4a8483fc30fe9c938f1a46ab23fc28d4d6222cd2 (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 Signed-off-by: Linnan Li <lilinnan@xiaomi.corp-partner.google.com> (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:5af92f97f44ff2b96fd6d78e0843f2f133627f88) Merged-In: Ic5e328f0e445e3271cb1797725c4c00589a6b5e1 Change-Id: Ic5e328f0e445e3271cb1797725c4c00589a6b5e1
-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 862e12899e..21882b5c83 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3933,6 +3933,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 f4f0bab25e..7415eb6ef8 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -8740,6 +8740,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) {