Merge "Move drag event to InputDispatcher (3/n)" into sc-dev
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index a19b04f..5270b8a 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -130,6 +130,23 @@
pointerCaptureEnabled ? "true" : "false");
}
+// --- DragEntry ---
+
+// Drag notifications always go to apps, so set the flag POLICY_FLAG_PASS_TO_USER for all entries
+DragEntry::DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting,
+ float x, float y)
+ : EventEntry(id, Type::DRAG, eventTime, POLICY_FLAG_PASS_TO_USER),
+ connectionToken(connectionToken),
+ isExiting(isExiting),
+ x(x),
+ y(y) {}
+
+DragEntry::~DragEntry() {}
+
+std::string DragEntry::getDescription() const {
+ return StringPrintf("DragEntry(isExiting=%s, x=%f, y=%f)", isExiting ? "true" : "false", x, y);
+}
+
// --- KeyEntry ---
KeyEntry::KeyEntry(int32_t id, nsecs_t eventTime, int32_t deviceId, uint32_t source,
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index ed17e68..e5fb26c 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -38,6 +38,7 @@
MOTION,
SENSOR,
POINTER_CAPTURE_CHANGED,
+ DRAG,
};
int32_t id;
@@ -111,6 +112,18 @@
virtual ~PointerCaptureChangedEntry();
};
+struct DragEntry : EventEntry {
+ sp<IBinder> connectionToken;
+ bool isExiting;
+ float x, y;
+
+ DragEntry(int32_t id, nsecs_t eventTime, sp<IBinder> connectionToken, bool isExiting, float x,
+ float y);
+ std::string getDescription() const override;
+
+ ~DragEntry() override;
+};
+
struct KeyEntry : EventEntry {
int32_t deviceId;
uint32_t source;
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 3183a98..fe46d17 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -753,6 +753,14 @@
break;
}
+ case EventEntry::Type::DRAG: {
+ std::shared_ptr<DragEntry> typedEntry =
+ std::static_pointer_cast<DragEntry>(mPendingEvent);
+ dispatchDragLocked(currentTime, typedEntry);
+ done = true;
+ break;
+ }
+
case EventEntry::Type::KEY: {
std::shared_ptr<KeyEntry> keyEntry = std::static_pointer_cast<KeyEntry>(mPendingEvent);
if (isAppSwitchDue) {
@@ -921,7 +929,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
// nothing to do
break;
}
@@ -943,7 +952,8 @@
sp<InputWindowHandle> InputDispatcher::findTouchedWindowAtLocked(int32_t displayId, int32_t x,
int32_t y, TouchState* touchState,
bool addOutsideTargets,
- bool addPortalWindows) {
+ bool addPortalWindows,
+ bool ignoreDragWindow) {
if ((addPortalWindows || addOutsideTargets) && touchState == nullptr) {
LOG_ALWAYS_FATAL(
"Must provide a valid touch state if adding portal windows or outside targets");
@@ -951,6 +961,9 @@
// Traverse windows from front to back to find touched window.
const std::vector<sp<InputWindowHandle>>& windowHandles = getWindowHandlesLocked(displayId);
for (const sp<InputWindowHandle>& windowHandle : windowHandles) {
+ if (ignoreDragWindow && haveSameToken(windowHandle, touchState->dragWindow)) {
+ continue;
+ }
const InputWindowInfo* windowInfo = windowHandle->getInfo();
if (windowInfo->displayId == displayId) {
auto flags = windowInfo->flags;
@@ -1060,7 +1073,8 @@
case EventEntry::Type::SENSOR: {
break;
}
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
break;
}
case EventEntry::Type::FOCUS:
@@ -1554,6 +1568,35 @@
return true;
}
+void InputDispatcher::enqueueDragEventLocked(const sp<InputWindowHandle>& windowHandle,
+ bool isExiting, const MotionEntry& motionEntry) {
+ // If the window needs enqueue a drag event, the pointerCount should be 1 and the action should
+ // be AMOTION_EVENT_ACTION_MOVE, that could guarantee the first pointer is always valid.
+ LOG_ALWAYS_FATAL_IF(motionEntry.pointerCount != 1);
+ PointerCoords pointerCoords;
+ pointerCoords.copyFrom(motionEntry.pointerCoords[0]);
+ pointerCoords.transform(windowHandle->getInfo()->transform);
+
+ std::unique_ptr<DragEntry> dragEntry =
+ std::make_unique<DragEntry>(mIdGenerator.nextId(), motionEntry.eventTime,
+ windowHandle->getToken(), isExiting, pointerCoords.getX(),
+ pointerCoords.getY());
+
+ enqueueInboundEventLocked(std::move(dragEntry));
+}
+
+void InputDispatcher::dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) {
+ std::shared_ptr<InputChannel> channel = getInputChannelLocked(entry->connectionToken);
+ if (channel == nullptr) {
+ return; // Window has gone away
+ }
+ InputTarget target;
+ target.inputChannel = channel;
+ target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
+ entry->dispatchInProgress = true;
+ dispatchEventLocked(currentTime, entry, {target});
+}
+
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
#if DEBUG_OUTBOUND_EVENT_DETAILS
ALOGD("%seventTime=%" PRId64 ", deviceId=%d, source=0x%x, displayId=%" PRId32
@@ -1660,7 +1703,8 @@
case EventEntry::Type::FOCUS:
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
- case EventEntry::Type::SENSOR: {
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::DRAG: {
ALOGE("%s events do not have a target display", NamedEnum::string(entry.type).c_str());
return ADISPLAY_ID_NONE;
}
@@ -2017,6 +2061,8 @@
goto Failed;
}
+ addDragEventLocked(entry, tempTouchState);
+
// Check whether touches should slip outside of the current foreground window.
if (maskedAction == AMOTION_EVENT_ACTION_MOVE && entry.pointerCount == 1 &&
tempTouchState.isSlippery()) {
@@ -2272,6 +2318,38 @@
return injectionResult;
}
+void InputDispatcher::addDragEventLocked(const MotionEntry& entry, TouchState& state) {
+ if (entry.pointerCount != 1 || !state.dragWindow) {
+ return;
+ }
+
+ int32_t maskedAction = entry.action & AMOTION_EVENT_ACTION_MASK;
+ int32_t x = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_X));
+ int32_t y = static_cast<int32_t>(entry.pointerCoords[0].getAxisValue(AMOTION_EVENT_AXIS_Y));
+ if (maskedAction == AMOTION_EVENT_ACTION_MOVE) {
+ const sp<InputWindowHandle> hoverWindowHandle =
+ findTouchedWindowAtLocked(entry.displayId, x, y, &state,
+ false /*addOutsideTargets*/, false /*addPortalWindows*/,
+ true /*ignoreDragWindow*/);
+ // enqueue drag exit if needed.
+ if (hoverWindowHandle != state.dragHoverWindowHandle &&
+ !haveSameToken(hoverWindowHandle, state.dragHoverWindowHandle)) {
+ if (state.dragHoverWindowHandle != nullptr) {
+ enqueueDragEventLocked(state.dragHoverWindowHandle, true /*isExiting*/, entry);
+ }
+ state.dragHoverWindowHandle = hoverWindowHandle;
+ }
+ // enqueue drag location if needed.
+ if (hoverWindowHandle != nullptr) {
+ enqueueDragEventLocked(hoverWindowHandle, false /*isExiting*/, entry);
+ }
+ } else if (maskedAction == AMOTION_EVENT_ACTION_UP ||
+ maskedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ state.dragWindow = nullptr;
+ state.dragHoverWindowHandle = nullptr;
+ }
+}
+
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
@@ -2543,7 +2621,8 @@
void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
if (eventEntry.type == EventEntry::Type::FOCUS ||
- eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED) {
+ eventEntry.type == EventEntry::Type::POINTER_CAPTURE_CHANGED ||
+ eventEntry.type == EventEntry::Type::DRAG) {
// Focus or pointer capture changed events are passed to apps, but do not represent user
// activity.
return;
@@ -2585,7 +2664,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s events are not user activity",
NamedEnum::string(eventEntry.type).c_str());
break;
@@ -2799,7 +2879,8 @@
break;
}
case EventEntry::Type::FOCUS:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
break;
}
case EventEntry::Type::SENSOR: {
@@ -3022,6 +3103,15 @@
break;
}
+ case EventEntry::Type::DRAG: {
+ const DragEntry& dragEntry = static_cast<const DragEntry&>(eventEntry);
+ status = connection->inputPublisher.publishDragEvent(dispatchEntry->seq,
+ dragEntry.id, dragEntry.x,
+ dragEntry.y,
+ dragEntry.isExiting);
+ break;
+ }
+
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::SENSOR: {
@@ -3319,7 +3409,8 @@
break;
}
case EventEntry::Type::FOCUS:
- case EventEntry::Type::POINTER_CAPTURE_CHANGED: {
+ case EventEntry::Type::POINTER_CAPTURE_CHANGED:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("Canceling %s events is not supported",
NamedEnum::string(cancelationEventEntry->type).c_str());
break;
@@ -3384,7 +3475,8 @@
case EventEntry::Type::CONFIGURATION_CHANGED:
case EventEntry::Type::DEVICE_RESET:
case EventEntry::Type::POINTER_CAPTURE_CHANGED:
- case EventEntry::Type::SENSOR: {
+ case EventEntry::Type::SENSOR:
+ case EventEntry::Type::DRAG: {
LOG_ALWAYS_FATAL("%s event should not be found inside Connections's queue",
NamedEnum::string(downEventEntry->type).c_str());
break;
@@ -4348,6 +4440,15 @@
++i;
}
}
+
+ // If drag window is gone, it would receive a cancel event and broadcast the DRAG_END. we
+ // could just clear the state here.
+ if (state.dragWindow &&
+ std::find(windowHandles.begin(), windowHandles.end(), state.dragWindow) ==
+ windowHandles.end()) {
+ state.dragWindow = nullptr;
+ state.dragHoverWindowHandle = nullptr;
+ }
}
// Release information for windows that are no longer present.
@@ -4538,7 +4639,8 @@
mBlockUntrustedTouchesMode = mode;
}
-bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) {
+bool InputDispatcher::transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop) {
if (fromToken == toToken) {
if (DEBUG_FOCUS) {
ALOGD("Trivial transfer to same window.");
@@ -4582,6 +4684,11 @@
InputTarget::FLAG_DISPATCH_AS_IS);
state.addOrUpdateWindow(toWindowHandle, newTargetFlags, pointerIds);
+ // Store the dragging window.
+ if (isDragDrop) {
+ state.dragWindow = toWindowHandle;
+ }
+
found = true;
goto Found;
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 83094c2..b2f3625 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -120,8 +120,8 @@
virtual void setMaximumObscuringOpacityForTouch(float opacity) override;
virtual void setBlockUntrustedTouchesMode(android::os::BlockUntrustedTouchesMode mode) override;
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken,
- const sp<IBinder>& toToken) override;
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop = false) override;
virtual base::Result<std::unique_ptr<InputChannel>> createInputChannel(
const std::string& name) override;
@@ -185,6 +185,9 @@
// Enqueues a focus event.
void enqueueFocusEventLocked(const sp<IBinder>& windowToken, bool hasFocus,
const std::string& reason) REQUIRES(mLock);
+ // Enqueues a drag event.
+ void enqueueDragEventLocked(const sp<InputWindowHandle>& windowToken, bool isExiting,
+ const MotionEntry& motionEntry) REQUIRES(mLock);
// Adds an event to a queue of recent events for debugging purposes.
void addRecentEventLocked(std::shared_ptr<EventEntry> entry) REQUIRES(mLock);
@@ -204,7 +207,8 @@
sp<InputWindowHandle> findTouchedWindowAtLocked(int32_t displayId, int32_t x, int32_t y,
TouchState* touchState,
bool addOutsideTargets = false,
- bool addPortalWindows = false) REQUIRES(mLock);
+ bool addPortalWindows = false,
+ bool ignoreDragWindow = false) REQUIRES(mLock);
// All registered connections mapped by channel file descriptor.
std::unordered_map<int, sp<Connection>> mConnectionsByFd GUARDED_BY(mLock);
@@ -387,6 +391,7 @@
const std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void dispatchSensorLocked(nsecs_t currentTime, std::shared_ptr<SensorEntry> entry,
DropReason* dropReason, nsecs_t* nextWakeupTime) REQUIRES(mLock);
+ void dispatchDragLocked(nsecs_t currentTime, std::shared_ptr<DragEntry> entry) REQUIRES(mLock);
void logOutboundKeyDetails(const char* prefix, const KeyEntry& entry);
void logOutboundMotionDetails(const char* prefix, const MotionEntry& entry);
@@ -489,10 +494,12 @@
std::vector<InputTarget>& inputTargets) REQUIRES(mLock);
void addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets, int32_t displayId,
float xOffset = 0, float yOffset = 0) REQUIRES(mLock);
-
void pokeUserActivityLocked(const EventEntry& eventEntry) REQUIRES(mLock);
bool checkInjectionPermission(const sp<InputWindowHandle>& windowHandle,
const InjectionState* injectionState);
+ // Enqueue a drag event if needed, and update the touch state.
+ // Uses findTouchedWindowTargetsLocked to make the decision
+ void addDragEventLocked(const MotionEntry& entry, TouchState& state) REQUIRES(mLock);
struct TouchOcclusionInfo {
bool hasBlockingOcclusion;
diff --git a/services/inputflinger/dispatcher/TouchState.cpp b/services/inputflinger/dispatcher/TouchState.cpp
index 81b3cf0..4165f49 100644
--- a/services/inputflinger/dispatcher/TouchState.cpp
+++ b/services/inputflinger/dispatcher/TouchState.cpp
@@ -49,6 +49,8 @@
windows = other.windows;
portalWindows = other.portalWindows;
gestureMonitors = other.gestureMonitors;
+ dragHoverWindowHandle = other.dragHoverWindowHandle;
+ dragWindow = other.dragWindow;
}
void TouchState::addOrUpdateWindow(const sp<InputWindowHandle>& windowHandle, int32_t targetFlags,
diff --git a/services/inputflinger/dispatcher/TouchState.h b/services/inputflinger/dispatcher/TouchState.h
index 623c6a8..d7a561c 100644
--- a/services/inputflinger/dispatcher/TouchState.h
+++ b/services/inputflinger/dispatcher/TouchState.h
@@ -41,6 +41,11 @@
std::vector<TouchedMonitor> gestureMonitors;
+ // The last drag hover window which could receive the drag event.
+ sp<InputWindowHandle> dragHoverWindowHandle;
+ // The window being dragged.
+ sp<InputWindowHandle> dragWindow;
+
TouchState();
~TouchState();
void reset();
diff --git a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
index 3491893..b601dfc 100644
--- a/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
+++ b/services/inputflinger/dispatcher/include/InputDispatcherInterface.h
@@ -149,8 +149,8 @@
*
* Returns true on success. False if the window did not actually have touch focus.
*/
- virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken) = 0;
-
+ virtual bool transferTouchFocus(const sp<IBinder>& fromToken, const sp<IBinder>& toToken,
+ bool isDragDrop) = 0;
/**
* Sets focus on the specified window.
*/
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 5cdcfaf..32f9b69 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -766,6 +766,9 @@
case AINPUT_EVENT_TYPE_CAPTURE: {
FAIL() << "Use 'consumeCaptureEvent' for CAPTURE events";
}
+ case AINPUT_EVENT_TYPE_DRAG: {
+ FAIL() << "Use 'consumeDragEvent' for DRAG events";
+ }
default: {
FAIL() << mName.c_str() << ": invalid event type: " << expectedEventType;
}
@@ -803,6 +806,23 @@
EXPECT_EQ(hasCapture, captureEvent.getPointerCaptureEnabled());
}
+ void consumeDragEvent(bool isExiting, float x, float y) {
+ const InputEvent* event = consume();
+ ASSERT_NE(nullptr, event) << mName.c_str()
+ << ": consumer should have returned non-NULL event.";
+ ASSERT_EQ(AINPUT_EVENT_TYPE_DRAG, event->getType())
+ << "Got " << inputEventTypeToString(event->getType())
+ << " event instead of DRAG event";
+
+ EXPECT_EQ(ADISPLAY_ID_NONE, event->getDisplayId())
+ << mName.c_str() << ": event displayId should always be NONE.";
+
+ const auto& dragEvent = static_cast<const DragEvent&>(*event);
+ EXPECT_EQ(isExiting, dragEvent.isExiting());
+ EXPECT_EQ(x, dragEvent.getX());
+ EXPECT_EQ(y, dragEvent.getY());
+ }
+
void assertNoEvents() {
InputEvent* event = consume();
if (event == nullptr) {
@@ -905,7 +925,7 @@
mInfo.frameTop = frame.top;
mInfo.frameRight = frame.right;
mInfo.frameBottom = frame.bottom;
- mInfo.transform.set(frame.left, frame.top);
+ mInfo.transform.set(-frame.left, -frame.top);
mInfo.touchableRegion.clear();
mInfo.addTouchableRegion(frame);
}
@@ -1003,6 +1023,10 @@
expectedFlags);
}
+ void consumeDragEvent(bool isExiting, float x, float y) {
+ mInputReceiver->consumeDragEvent(isExiting, x, y);
+ }
+
std::optional<uint32_t> receiveEvent(InputEvent** outEvent = nullptr) {
if (mInputReceiver == nullptr) {
ADD_FAILURE() << "Invalid receive event on window with no receiver";
@@ -4682,4 +4706,87 @@
mTouchWindow->consumeAnyMotionDown();
}
+class InputDispatcherDragTests : public InputDispatcherTest {
+protected:
+ std::shared_ptr<FakeApplicationHandle> mApp;
+ sp<FakeWindowHandle> mWindow;
+ sp<FakeWindowHandle> mSecondWindow;
+ sp<FakeWindowHandle> mDragWindow;
+
+ void SetUp() override {
+ InputDispatcherTest::SetUp();
+ mApp = std::make_shared<FakeApplicationHandle>();
+ mWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow", ADISPLAY_ID_DEFAULT);
+ mWindow->setFrame(Rect(0, 0, 100, 100));
+ mWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mSecondWindow = new FakeWindowHandle(mApp, mDispatcher, "TestWindow2", ADISPLAY_ID_DEFAULT);
+ mSecondWindow->setFrame(Rect(100, 0, 200, 100));
+ mSecondWindow->setFlags(InputWindowInfo::Flag::NOT_TOUCH_MODAL);
+
+ mDispatcher->setFocusedApplication(ADISPLAY_ID_DEFAULT, mApp);
+ mDispatcher->setInputWindows({{ADISPLAY_ID_DEFAULT, {mWindow, mSecondWindow}}});
+ }
+
+ // Start performing drag, we will create a drag window and transfer touch to it.
+ void performDrag() {
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionDown(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT,
+ {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+
+ // Window should receive motion event.
+ mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+
+ // The drag window covers the entire display
+ mDragWindow = new FakeWindowHandle(mApp, mDispatcher, "DragWindow", ADISPLAY_ID_DEFAULT);
+ mDispatcher->setInputWindows(
+ {{ADISPLAY_ID_DEFAULT, {mDragWindow, mWindow, mSecondWindow}}});
+
+ // Transfer touch focus to the drag window
+ mDispatcher->transferTouchFocus(mWindow->getToken(), mDragWindow->getToken(),
+ true /* isDragDrop */);
+ mWindow->consumeMotionCancel();
+ mDragWindow->consumeMotionDown();
+ }
+};
+
+TEST_F(InputDispatcherDragTests, DragEnterAndDragExit) {
+ performDrag();
+
+ // Move on window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->assertNoEvents();
+
+ // Move to another window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {150, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(true, 150, 50);
+ mSecondWindow->consumeDragEvent(false, 50, 50);
+
+ // Move back to original window.
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionEvent(mDispatcher, AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
+ mWindow->consumeDragEvent(false, 50, 50);
+ mSecondWindow->consumeDragEvent(true, -50, 50);
+
+ ASSERT_EQ(InputEventInjectionResult::SUCCEEDED,
+ injectMotionUp(mDispatcher, AINPUT_SOURCE_TOUCHSCREEN, ADISPLAY_ID_DEFAULT, {50, 50}))
+ << "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
+ mDragWindow->consumeMotionUp(ADISPLAY_ID_DEFAULT);
+ mWindow->assertNoEvents();
+ mSecondWindow->assertNoEvents();
+}
+
} // namespace android::inputdispatcher