Associate device id with isSlippery
Because we have supported multi-device event streams in the
InputDispatcher, but there are still some logics that use the previous
single-device stream logic. Here, associating isSlippery with deviceId
can solve event slip to wrong window.
Bug: 328553381
Test: atest inputflinger_tests
Change-Id: I7319d7e7f897c86975708ff3063a52b48c91888b
Signed-off-by: Linnan Li <lilinnan@xiaomi.corp-partner.google.com>
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ccd28f3..bc173b1 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -7328,6 +7328,154 @@
appearingWindow->assertNoEvents();
}
+/**
+ * Three windows:
+ * - left window, which has FLAG_SLIPPERY, so it supports slippery exit
+ * - right window
+ * - spy window
+ * The three windows do not overlap.
+ *
+ * We have two devices reporting events:
+ * - Device A reports ACTION_DOWN, which lands in the left window
+ * - Device B reports ACTION_DOWN, which lands in the spy window.
+ * - Now, device B reports ACTION_MOVE events which move to the right window.
+ *
+ * The right window should not receive any events because the spy window is not a foreground window,
+ * and also it does not support slippery touches.
+ */
+TEST_F(InputDispatcherTest, MultiDeviceSpyWindowSlipTest) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWindow->setSlippery(true);
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
+ ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(100, 0, 200, 100));
+
+ sp<FakeWindowHandle> spyWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Spy Window", ADISPLAY_ID_DEFAULT);
+ spyWindow->setFrame(Rect(200, 0, 300, 100));
+ spyWindow->setSpy(true);
+ spyWindow->setTrustedOverlay(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *rightWindow->getInfo(), *spyWindow->getInfo()}, {}, 0, 0});
+
+ const DeviceId deviceA = 9;
+ const DeviceId deviceB = 3;
+
+ // Tap on left window with device A
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+
+ // Tap on spy window with device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(50))
+ .deviceId(deviceB)
+ .build());
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+
+ // Move to right window with device B. Touches should not slip to the right window, because spy
+ // window is not a foreground window, and it does not have FLAG_SLIPPERY
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceB)
+ .build());
+ leftWindow->assertNoEvents();
+ rightWindow->assertNoEvents();
+ spyWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceB)));
+}
+
+/**
+ * Three windows arranged horizontally and without any overlap.
+ * The left and right windows have FLAG_SLIPPERY. The middle window does not have any special flags.
+ *
+ * We have two devices reporting events:
+ * - Device A reports ACTION_DOWN which lands in the left window
+ * - Device B reports ACTION_DOWN which lands in the right window
+ * - Device B reports ACTION_MOVE that shifts to the middle window.
+ * This should cause touches for Device B to slip from the right window to the middle window.
+ * The right window should receive ACTION_CANCEL for device B and the
+ * middle window should receive down event for Device B.
+ * If device B reports more ACTION_MOVE events, the middle window should receive remaining events.
+ */
+TEST_F(InputDispatcherTest, MultiDeviceSlipperyWindowTest) {
+ std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
+ sp<FakeWindowHandle> leftWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Left window",
+ ADISPLAY_ID_DEFAULT);
+ leftWindow->setFrame(Rect(0, 0, 100, 100));
+ leftWindow->setSlippery(true);
+
+ sp<FakeWindowHandle> middleWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "middle window",
+ ADISPLAY_ID_DEFAULT);
+ middleWindow->setFrame(Rect(100, 0, 200, 100));
+
+ sp<FakeWindowHandle> rightWindow =
+ sp<FakeWindowHandle>::make(application, mDispatcher, "Right window",
+ ADISPLAY_ID_DEFAULT);
+ rightWindow->setFrame(Rect(200, 0, 300, 100));
+ rightWindow->setSlippery(true);
+
+ mDispatcher->onWindowInfosChanged(
+ {{*leftWindow->getInfo(), *middleWindow->getInfo(), *rightWindow->getInfo()},
+ {},
+ 0,
+ 0});
+
+ const DeviceId deviceA = 9;
+ const DeviceId deviceB = 3;
+
+ // Tap on left window with device A
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(50).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+
+ // Tap on right window with device B
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(250).y(50))
+ .deviceId(deviceB)
+ .build());
+ rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+
+ // Move to middle window with device B. Touches should slip to middle window, because right
+ // window is a foreground window that's associated with device B and has FLAG_SLIPPERY.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceB)
+ .build());
+ rightWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceB)));
+ middleWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceB)));
+
+ // Move to middle window with device A. Touches should slip to middle window, because left
+ // window is a foreground window that's associated with device A and has FLAG_SLIPPERY.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(150).y(50))
+ .deviceId(deviceA)
+ .build());
+ leftWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_CANCEL), WithDeviceId(deviceA)));
+ middleWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN), WithDeviceId(deviceA)));
+
+ // Ensure that middle window can receive the remaining move events.
+ mDispatcher->notifyMotion(MotionArgsBuilder(ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN)
+ .pointer(PointerBuilder(0, ToolType::FINGER).x(151).y(51))
+ .deviceId(deviceB)
+ .build());
+ leftWindow->assertNoEvents();
+ middleWindow->consumeMotionEvent(AllOf(WithMotionAction(ACTION_MOVE), WithDeviceId(deviceB)));
+ rightWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherTest, NotifiesDeviceInteractionsWithMotions) {
using Uid = gui::Uid;
std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();