summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp154
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.h6
-rw-r--r--services/inputflinger/dispatcher/InputTarget.cpp39
-rw-r--r--services/inputflinger/dispatcher/InputTarget.h47
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp103
5 files changed, 290 insertions, 59 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 26c2d3f758..0178811f0f 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -256,6 +256,67 @@ static bool isStaleEvent(nsecs_t currentTime, const EventEntry& entry) {
return currentTime - entry.eventTime >= STALE_EVENT_TIMEOUT;
}
+static std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+ EventEntry* eventEntry,
+ int32_t inputTargetFlags) {
+ if (inputTarget.useDefaultPointerInfo()) {
+ const PointerInfo& pointerInfo = inputTarget.getDefaultPointerInfo();
+ return std::make_unique<DispatchEntry>(eventEntry, // increments ref
+ inputTargetFlags, pointerInfo.xOffset,
+ pointerInfo.yOffset, inputTarget.globalScaleFactor,
+ pointerInfo.windowXScale, pointerInfo.windowYScale);
+ }
+
+ ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
+
+ PointerCoords pointerCoords[MAX_POINTERS];
+
+ // Use the first pointer information to normalize all other pointers. This could be any pointer
+ // as long as all other pointers are normalized to the same value and the final DispatchEntry
+ // uses the offset and scale for the normalized pointer.
+ const PointerInfo& firstPointerInfo =
+ inputTarget.pointerInfos[inputTarget.pointerIds.firstMarkedBit()];
+
+ // Iterate through all pointers in the event to normalize against the first.
+ for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.pointerCount; pointerIndex++) {
+ const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
+ uint32_t pointerId = uint32_t(pointerProperties.id);
+ const PointerInfo& currPointerInfo = inputTarget.pointerInfos[pointerId];
+
+ // The scale factor is the ratio of the current pointers scale to the normalized scale.
+ float scaleXDiff = currPointerInfo.windowXScale / firstPointerInfo.windowXScale;
+ float scaleYDiff = currPointerInfo.windowYScale / firstPointerInfo.windowYScale;
+
+ pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
+ // First apply the current pointers offset to set the window at 0,0
+ pointerCoords[pointerIndex].applyOffset(currPointerInfo.xOffset, currPointerInfo.yOffset);
+ // Next scale the coordinates.
+ pointerCoords[pointerIndex].scale(1, scaleXDiff, scaleYDiff);
+ // Lastly, offset the coordinates so they're in the normalized pointer's frame.
+ pointerCoords[pointerIndex].applyOffset(-firstPointerInfo.xOffset,
+ -firstPointerInfo.yOffset);
+ }
+
+ MotionEntry* combinedMotionEntry =
+ new MotionEntry(motionEntry.sequenceNum, motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId, motionEntry.policyFlags,
+ motionEntry.action, motionEntry.actionButton, motionEntry.flags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ motionEntry.downTime, motionEntry.pointerCount,
+ motionEntry.pointerProperties, pointerCoords, 0 /* xOffset */,
+ 0 /* yOffset */);
+
+ return std::make_unique<DispatchEntry>(combinedMotionEntry, // increments ref
+ inputTargetFlags, firstPointerInfo.xOffset,
+ firstPointerInfo.yOffset, inputTarget.globalScaleFactor,
+ firstPointerInfo.windowXScale,
+ firstPointerInfo.windowYScale);
+}
+
// --- InputDispatcherThread ---
class InputDispatcher::InputDispatcherThread : public Thread {
@@ -1111,7 +1172,7 @@ void InputDispatcher::dispatchEventLocked(nsecs_t currentTime, EventEntry* event
sp<Connection> connection =
getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
if (connection != nullptr) {
- prepareDispatchCycleLocked(currentTime, connection, eventEntry, &inputTarget);
+ prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
} else {
if (DEBUG_FOCUS) {
ALOGD("Dropping event delivery to target with channel '%s' because it "
@@ -1785,23 +1846,34 @@ Unresponsive:
void InputDispatcher::addWindowTargetLocked(const sp<InputWindowHandle>& windowHandle,
int32_t targetFlags, BitSet32 pointerIds,
std::vector<InputTarget>& inputTargets) {
- sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
- if (inputChannel == nullptr) {
- ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
- return;
- }
+ std::vector<InputTarget>::iterator it =
+ std::find_if(inputTargets.begin(), inputTargets.end(),
+ [&windowHandle](const InputTarget& inputTarget) {
+ return inputTarget.inputChannel->getConnectionToken() ==
+ windowHandle->getToken();
+ });
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- InputTarget target;
- target.inputChannel = inputChannel;
- target.flags = targetFlags;
- target.xOffset = -windowInfo->frameLeft;
- target.yOffset = -windowInfo->frameTop;
- target.globalScaleFactor = windowInfo->globalScaleFactor;
- target.windowXScale = windowInfo->windowXScale;
- target.windowYScale = windowInfo->windowYScale;
- target.pointerIds = pointerIds;
- inputTargets.push_back(target);
+
+ if (it == inputTargets.end()) {
+ InputTarget inputTarget;
+ sp<InputChannel> inputChannel = getInputChannelLocked(windowHandle->getToken());
+ if (inputChannel == nullptr) {
+ ALOGW("Window %s already unregistered input channel", windowHandle->getName().c_str());
+ return;
+ }
+ inputTarget.inputChannel = inputChannel;
+ inputTarget.flags = targetFlags;
+ inputTarget.globalScaleFactor = windowInfo->globalScaleFactor;
+ inputTargets.push_back(inputTarget);
+ it = inputTargets.end() - 1;
+ }
+
+ ALOG_ASSERT(it->flags == targetFlags);
+ ALOG_ASSERT(it->globalScaleFactor == windowInfo->globalScaleFactor);
+
+ it->addPointers(pointerIds, -windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
}
void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>& inputTargets,
@@ -1824,10 +1896,7 @@ void InputDispatcher::addMonitoringTargetLocked(const Monitor& monitor, float xO
InputTarget target;
target.inputChannel = monitor.inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
- target.xOffset = xOffset;
- target.yOffset = yOffset;
- target.pointerIds.clear();
- target.globalScaleFactor = 1.0f;
+ target.setDefaultPointerInfo(xOffset, yOffset, 1 /* windowXScale */, 1 /* windowYScale */);
inputTargets.push_back(target);
}
@@ -2045,7 +2114,7 @@ void InputDispatcher::pokeUserActivityLocked(const EventEntry& eventEntry) {
void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
const sp<Connection>& connection,
EventEntry* eventEntry,
- const InputTarget* inputTarget) {
+ const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
std::string message =
StringPrintf("prepareDispatchCycleLocked(inputChannel=%s, sequenceNum=%" PRIu32 ")",
@@ -2056,9 +2125,9 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=0x%08x, "
"xOffset=%f, yOffset=%f, globalScaleFactor=%f, "
"windowScaleFactor=(%f, %f), pointerIds=0x%x",
- connection->getInputChannelName().c_str(), inputTarget->flags, inputTarget->xOffset,
- inputTarget->yOffset, inputTarget->globalScaleFactor, inputTarget->windowXScale,
- inputTarget->windowYScale, inputTarget->pointerIds.value);
+ connection->getInputChannelName().c_str(), inputTarget.flags, inputTarget.xOffset,
+ inputTarget.yOffset, inputTarget.globalScaleFactor, inputTarget.windowXScale,
+ inputTarget.windowYScale, inputTarget.pointerIds.value);
#endif
// Skip this event if the connection status is not normal.
@@ -2072,13 +2141,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
}
// Split a motion event if needed.
- if (inputTarget->flags & InputTarget::FLAG_SPLIT) {
+ if (inputTarget.flags & InputTarget::FLAG_SPLIT) {
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
- if (inputTarget->pointerIds.count() != originalMotionEntry.pointerCount) {
+ if (inputTarget.pointerIds.count() != originalMotionEntry.pointerCount) {
MotionEntry* splitMotionEntry =
- splitMotionEvent(originalMotionEntry, inputTarget->pointerIds);
+ splitMotionEvent(originalMotionEntry, inputTarget.pointerIds);
if (!splitMotionEntry) {
return; // split event was dropped
}
@@ -2100,7 +2169,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
const sp<Connection>& connection,
EventEntry* eventEntry,
- const InputTarget* inputTarget) {
+ const InputTarget& inputTarget) {
if (ATRACE_ENABLED()) {
std::string message =
StringPrintf("enqueueDispatchEntriesLocked(inputChannel=%s, sequenceNum=%" PRIu32
@@ -2133,7 +2202,7 @@ void InputDispatcher::enqueueDispatchEntriesLocked(nsecs_t currentTime,
void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connection,
EventEntry* eventEntry,
- const InputTarget* inputTarget,
+ const InputTarget& inputTarget,
int32_t dispatchMode) {
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("enqueueDispatchEntry(inputChannel=%s, dispatchMode=%s)",
@@ -2141,7 +2210,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio
dispatchModeToString(dispatchMode).c_str());
ATRACE_NAME(message.c_str());
}
- int32_t inputTargetFlags = inputTarget->flags;
+ int32_t inputTargetFlags = inputTarget.flags;
if (!(inputTargetFlags & dispatchMode)) {
return;
}
@@ -2149,11 +2218,8 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
- DispatchEntry* dispatchEntry =
- new DispatchEntry(eventEntry, // increments ref
- inputTargetFlags, inputTarget->xOffset, inputTarget->yOffset,
- inputTarget->globalScaleFactor, inputTarget->windowXScale,
- inputTarget->windowYScale);
+ std::unique_ptr<DispatchEntry> dispatchEntry =
+ createDispatchEntry(inputTarget, eventEntry, inputTargetFlags);
// Apply target flags and update the connection's input state.
switch (eventEntry->type) {
@@ -2168,7 +2234,6 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio
ALOGD("channel '%s' ~ enqueueDispatchEntryLocked: skipping inconsistent key event",
connection->getInputChannelName().c_str());
#endif
- delete dispatchEntry;
return; // skip the inconsistent event
}
break;
@@ -2215,12 +2280,11 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio
"event",
connection->getInputChannelName().c_str());
#endif
- delete dispatchEntry;
return; // skip the inconsistent event
}
dispatchPointerDownOutsideFocus(motionEntry.source, dispatchEntry->resolvedAction,
- inputTarget->inputChannel->getConnectionToken());
+ inputTarget.inputChannel->getConnectionToken());
break;
}
@@ -2238,7 +2302,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const sp<Connection>& connectio
}
// Enqueue the dispatch entry.
- connection->outboundQueue.push_back(dispatchEntry);
+ connection->outboundQueue.push_back(dispatchEntry.release());
traceOutboundQueueLength(connection);
}
@@ -2611,21 +2675,15 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
getWindowHandleLocked(connection->inputChannel->getConnectionToken());
if (windowHandle != nullptr) {
const InputWindowInfo* windowInfo = windowHandle->getInfo();
- target.xOffset = -windowInfo->frameLeft;
- target.yOffset = -windowInfo->frameTop;
+ target.setDefaultPointerInfo(-windowInfo->frameLeft, -windowInfo->frameTop,
+ windowInfo->windowXScale, windowInfo->windowYScale);
target.globalScaleFactor = windowInfo->globalScaleFactor;
- target.windowXScale = windowInfo->windowXScale;
- target.windowYScale = windowInfo->windowYScale;
- } else {
- target.xOffset = 0;
- target.yOffset = 0;
- target.globalScaleFactor = 1.0f;
}
target.inputChannel = connection->inputChannel;
target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
enqueueDispatchEntryLocked(connection, cancelationEventEntry, // increments ref
- &target, InputTarget::FLAG_DISPATCH_AS_IS);
+ target, InputTarget::FLAG_DISPATCH_AS_IS);
cancelationEventEntry->release();
}
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index f9eca01b2e..50b52502a0 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -378,13 +378,13 @@ private:
// with the mutex held makes it easier to ensure that connection invariants are maintained.
// If needed, the methods post commands to run later once the critical bits are done.
void prepareDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget)
+ EventEntry* eventEntry, const InputTarget& inputTarget)
REQUIRES(mLock);
void enqueueDispatchEntriesLocked(nsecs_t currentTime, const sp<Connection>& connection,
- EventEntry* eventEntry, const InputTarget* inputTarget)
+ EventEntry* eventEntry, const InputTarget& inputTarget)
REQUIRES(mLock);
void enqueueDispatchEntryLocked(const sp<Connection>& connection, EventEntry* eventEntry,
- const InputTarget* inputTarget, int32_t dispatchMode)
+ const InputTarget& inputTarget, int32_t dispatchMode)
REQUIRES(mLock);
void startDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection)
REQUIRES(mLock);
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 80fa2cb995..bd7cb3ed9b 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -42,4 +42,43 @@ std::string dispatchModeToString(int32_t dispatchMode) {
return StringPrintf("%" PRId32, dispatchMode);
}
+void InputTarget::addPointers(BitSet32 newPointerIds, float xOffset, float yOffset,
+ float windowXScale, float windowYScale) {
+ // The pointerIds can be empty, but still a valid InputTarget. This can happen for Monitors
+ // and non splittable windows since we will just use all the pointers from the input event.
+ if (newPointerIds.isEmpty()) {
+ setDefaultPointerInfo(xOffset, yOffset, windowXScale, windowYScale);
+ return;
+ }
+
+ // Ensure that the new set of pointers doesn't overlap with the current set of pointers.
+ ALOG_ASSERT((pointerIds & newPointerIds) == 0);
+
+ pointerIds |= newPointerIds;
+ while (!newPointerIds.isEmpty()) {
+ int32_t pointerId = newPointerIds.clearFirstMarkedBit();
+ pointerInfos[pointerId].xOffset = xOffset;
+ pointerInfos[pointerId].yOffset = yOffset;
+ pointerInfos[pointerId].windowXScale = windowXScale;
+ pointerInfos[pointerId].windowYScale = windowYScale;
+ }
+}
+
+void InputTarget::setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+ float windowYScale) {
+ pointerIds.clear();
+ pointerInfos[0].xOffset = xOffset;
+ pointerInfos[0].yOffset = yOffset;
+ pointerInfos[0].windowXScale = windowXScale;
+ pointerInfos[0].windowYScale = windowYScale;
+}
+
+bool InputTarget::useDefaultPointerInfo() const {
+ return pointerIds.isEmpty();
+}
+
+const PointerInfo& InputTarget::getDefaultPointerInfo() const {
+ return pointerInfos[0];
+}
+
} // namespace android::inputdispatcher
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 2e9bca251f..fd98df1782 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -24,6 +24,22 @@
namespace android::inputdispatcher {
/*
+ * Information about each pointer for an InputTarget. This includes offset and scale so
+ * all pointers can be normalized to a single offset and scale.
+ */
+struct PointerInfo {
+ // The x and y offset to add to a MotionEvent as it is delivered.
+ // (ignored for KeyEvents)
+ float xOffset = 0.0f;
+ float yOffset = 0.0f;
+
+ // Scaling factor to apply to MotionEvent as it is delivered.
+ // (ignored for KeyEvents)
+ float windowXScale = 1.0f;
+ float windowYScale = 1.0f;
+};
+
+/*
* An input target specifies how an input event is to be dispatched to a particular window
* including the window's input channel, control flags, a timeout, and an X / Y offset to
* be added to input event coordinates to compensate for the absolute position of the
@@ -95,20 +111,35 @@ struct InputTarget {
// Flags for the input target.
int32_t flags = 0;
- // The x and y offset to add to a MotionEvent as it is delivered.
- // (ignored for KeyEvents)
- float xOffset = 0.0f;
- float yOffset = 0.0f;
-
// Scaling factor to apply to MotionEvent as it is delivered.
// (ignored for KeyEvents)
float globalScaleFactor = 1.0f;
- float windowXScale = 1.0f;
- float windowYScale = 1.0f;
// The subset of pointer ids to include in motion events dispatched to this input target
// if FLAG_SPLIT is set.
- BitSet32 pointerIds{};
+ BitSet32 pointerIds;
+ // The data is stored by the pointerId. Use the marked bits in pointerIds to look up PointerInfo
+ // per pointerId.
+ PointerInfo pointerInfos[MAX_POINTERS];
+
+ void addPointers(BitSet32 pointerIds, float xOffset, float yOffset, float windowXScale,
+ float windowYScale);
+ void setDefaultPointerInfo(float xOffset, float yOffset, float windowXScale,
+ float windowYScale);
+
+ /**
+ * Returns whether the default pointer information should be used. This will be true when the
+ * InputTarget doesn't have any bits set in the pointerIds bitset. This can happen for monitors
+ * and non splittable windows since we want all pointers for the EventEntry to go to this
+ * target.
+ */
+ bool useDefaultPointerInfo() const;
+
+ /**
+ * Returns the default PointerInfo object. This should be used when useDefaultPointerInfo is
+ * true.
+ */
+ const PointerInfo& getDefaultPointerInfo() const;
};
std::string dispatchModeToString(int32_t dispatchMode);
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index 5ffc89d210..bd8d2a4d39 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -1439,4 +1439,107 @@ TEST_F(InputDispatcherMultiWindowSameTokenTests, SingleTouchDifferentScale) {
consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, {expectedPoint});
}
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleTouchMoveDifferentScale) {
+ mWindow2->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
+TEST_F(InputDispatcherMultiWindowSameTokenTests, MultipleWindowsFirstTouchWithScale) {
+ mWindow1->setWindowScale(0.5f, 0.5f);
+
+ // Touch Window 1
+ std::vector<PointF> touchedPoints = {PointF{10, 10}};
+ std::vector<PointF> expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0])};
+
+ NotifyMotionArgs motionArgs =
+ generateMotionArgs(AMOTION_EVENT_ACTION_DOWN, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_DOWN, expectedPoints);
+
+ // Touch Window 2
+ int32_t actionPointerDown =
+ AMOTION_EVENT_ACTION_POINTER_DOWN + (1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT);
+ touchedPoints.emplace_back(PointF{150, 150});
+ expectedPoints.emplace_back(getPointInWindow(mWindow2->getInfo(), touchedPoints[1]));
+
+ motionArgs = generateMotionArgs(actionPointerDown, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ // Consuming from window1 since it's the window that has the InputReceiver
+ consumeMotionEvent(mWindow1, actionPointerDown, expectedPoints);
+
+ // Move both windows
+ touchedPoints = {{20, 20}, {175, 175}};
+ expectedPoints = {getPointInWindow(mWindow1->getInfo(), touchedPoints[0]),
+ getPointInWindow(mWindow2->getInfo(), touchedPoints[1])};
+
+ motionArgs = generateMotionArgs(AMOTION_EVENT_ACTION_MOVE, AINPUT_SOURCE_TOUCHSCREEN,
+ ADISPLAY_ID_DEFAULT, touchedPoints);
+ mDispatcher->notifyMotion(&motionArgs);
+
+ consumeMotionEvent(mWindow1, AMOTION_EVENT_ACTION_MOVE, expectedPoints);
+}
+
} // namespace android::inputdispatcher