Merge "Cancel drag and drop when pointers are canceled for the drag window" into main am: 939393d987
Original change: https://android-review.googlesource.com/c/platform/frameworks/native/+/2658296
Change-Id: I14ac58796ea522c494bbed3132aee67d965c13bc
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 66052e4..901c806 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3900,6 +3900,16 @@
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 ddb419b..f3aba79 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -8752,6 +8752,75 @@
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(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) {