Call pilfer pointers when second pointer goes down during D&D
We added new functionality to pilferPointers API in ag/18234604.
Using it we can extend the functionality of drag and drop to
allow user to interact with the UI while using 1 pointer/finger
to drag. Pilfering pointers allows gesture monitors to restart
gesture detection with new pointers that down.
Avoid pilfering right after transferTouch on D&D to prevent
security bug: Apps can misuse startDragAndDrop() API to keep
pilfering pointers for gesture monitors and user can get stuck
in an app. Instead we will pilfer when a second pointer goes down
indicating user want to interact while using D&D.
DD: go/global_drag_and_drop
Bug: 220109830
Test: atest inputflinger_tests
Change-Id: I956f040ff307021bb2f74a06228c6addb90e2168
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 0b3b770..5e92f0d 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -1657,6 +1657,13 @@
InputEventInjectionResult injectionResult;
if (isPointerEvent) {
// Pointer event. (eg. touchscreen)
+
+ if (mDragState &&
+ (entry->action & AMOTION_EVENT_ACTION_MASK) == AMOTION_EVENT_ACTION_POINTER_DOWN) {
+ // If drag and drop ongoing and pointer down occur: pilfer drag window pointers
+ pilferPointersLocked(mDragState->dragWindow->getToken());
+ }
+
injectionResult =
findTouchedWindowTargetsLocked(currentTime, *entry, inputTargets, nextWakeupTime,
&conflictingPointerActions);
@@ -5575,7 +5582,10 @@
status_t InputDispatcher::pilferPointers(const sp<IBinder>& token) {
std::scoped_lock _l(mLock);
+ return pilferPointersLocked(token);
+}
+status_t InputDispatcher::pilferPointersLocked(const sp<IBinder>& token) {
const std::shared_ptr<InputChannel> requestingChannel = getInputChannelLocked(token);
if (!requestingChannel) {
ALOGW("Attempted to pilfer pointers from an un-registered channel or invalid token");
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 44a6dc3..50124a6 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -255,6 +255,8 @@
void removeConnectionLocked(const sp<Connection>& connection) REQUIRES(mLock);
+ status_t pilferPointersLocked(const sp<IBinder>& token) REQUIRES(mLock);
+
template <typename T>
struct StrongPointerHash {
std::size_t operator()(const sp<T>& b) const { return std::hash<T*>{}(b.get()); }
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 0c0f9f8..8af7cc3 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -6094,6 +6094,7 @@
sp<FakeWindowHandle> mWindow;
sp<FakeWindowHandle> mSecondWindow;
sp<FakeWindowHandle> mDragWindow;
+ sp<FakeWindowHandle> mSpyWindow;
void SetUp() override {
InputDispatcherTest::SetUp();
@@ -6104,8 +6105,13 @@
mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
mSecondWindow->setFrame(Rect(100, 0, 200, 100));
+ mSpyWindow = new FakeWindowHandle(mApp, mDispatcher, "SpyWindow", ADISPLAY_ID_DEFAULT);
+ mSpyWindow->setSpy(true);
+ mSpyWindow->setTrustedOverlay(true);
+ mSpyWindow->setFrame(Rect(0, 0, 200, 100));
+
mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
- mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mSpyWindow, mWindow, mSecondWindow}}});
}
void injectDown() {
@@ -6116,6 +6122,8 @@
// Window should receive motion event.
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ // Spy window should also receive motion event
+ mSpyWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
}
// Start performing drag, we will create a drag window and transfer touch to it.
@@ -6128,8 +6136,9 @@
// The drag window covers the entire display
mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+ mDragWindow->setTouchableRegion(Region{{0, 0, 0, 0}});
mDispatcher->setInputWindows(
- {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+ {{ADISPLAY_ID_DEFAULT, {mDragWindow, mSpyWindow, mWindow, mSecondWindow}}});
// Transfer touch focus to the drag window
bool transferred =
@@ -6207,6 +6216,30 @@
mSecondWindow->assertNoEvents();
}
+TEST_F(InputDispatcherDragTests, DragEnterAndPointerDownPilfersPointers) {
+ performDrag();
+
+ // No cancel event after drag start
+ mSpyWindow->assertNoEvents();
+
+ const MotionEvent secondFingerDownEvent =
+ MotionEventBuilder(POINTER_1_DOWN, AINPUT_SOURCE_TOUCHSCREEN)
+ .eventTime(systemTime(SYSTEM_TIME_MONOTONIC))
+ .pointer(PointerBuilder(/* id */ 0, AMOTION_EVENT_TOOL_TYPE_FINGER).x(50).y(50))
+ .pointer(PointerBuilder(/* id */ 1, AMOTION_EVENT_TOOL_TYPE_FINGER).x(60).y(60))
+ .build();
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, secondFingerDownEvent, INJECT_EVENT_TIMEOUT,
+ InputEventInjectionSync::WAIT_FOR_RESULT))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Receives cancel for first pointer after next pointer down
+ mSpyWindow->consumeMotionCancel();
+ mSpyWindow->consumeMotionDown();
+
+ mSpyWindow->assertNoEvents();
+}
+
TEST_F(InputDispatcherDragTests, DragAndDrop) {
performDrag();