summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPrabir Pradhan <prabirmsp@google.com>2024-02-22 03:14:17 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2024-02-22 03:14:17 +0000
commit7438201514d9a11995dff49c6af3dd3045b08098 (patch)
treebb794cecc2e6852dc5de1f3b8984b8fed9d9f035
parent77add0b0f163a11792da4e2f0e694ed34e87589f (diff)
parent65a071a788a1f5e575d85460d08182d0726abc83 (diff)
downloadnative-7438201514d9a11995dff49c6af3dd3045b08098.tar.gz
Merge changes I2ab660ed,I308c12e2,I8c50a597,I14f77d61 into main
* changes: InputDispatcher_test: Verify all traced events match exactly InputDispatcher_test: Consume all moves to prevent events from batching InputDispatcher: Create new EventEntries for events with zero-ed coords InputDispatcher: Improve pointer transform mapping in InputTarget
-rw-r--r--include/input/Input.h10
-rw-r--r--libs/input/Input.cpp27
-rw-r--r--services/inputflinger/dispatcher/InputDispatcher.cpp105
-rw-r--r--services/inputflinger/dispatcher/InputTarget.cpp62
-rw-r--r--services/inputflinger/dispatcher/InputTarget.h27
-rw-r--r--services/inputflinger/tests/FakeInputTracingBackend.cpp62
-rw-r--r--services/inputflinger/tests/FakeInputTracingBackend.h4
-rw-r--r--services/inputflinger/tests/InputDispatcher_test.cpp44
8 files changed, 236 insertions, 105 deletions
diff --git a/include/input/Input.h b/include/input/Input.h
index 7b253a53ef..a84dcfc63c 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -180,7 +180,8 @@ static constexpr size_t MAX_POINTERS = 16;
* Declare a concrete type for the NDK's input event forward declaration.
*/
struct AInputEvent {
- virtual ~AInputEvent() { }
+ virtual ~AInputEvent() {}
+ bool operator==(const AInputEvent&) const = default;
};
/*
@@ -545,6 +546,8 @@ public:
static int32_t nextId();
+ bool operator==(const InputEvent&) const = default;
+
protected:
void initialize(int32_t id, DeviceId deviceId, uint32_t source, int32_t displayId,
std::array<uint8_t, 32> hmac);
@@ -598,6 +601,8 @@ public:
static const char* actionToString(int32_t action);
+ bool operator==(const KeyEvent&) const = default;
+
protected:
int32_t mAction;
int32_t mFlags;
@@ -917,6 +922,9 @@ public:
// The rounding precision for transformed motion events.
static constexpr float ROUNDING_PRECISION = 0.001f;
+ bool operator==(const MotionEvent&) const;
+ inline bool operator!=(const MotionEvent& o) const { return !(*this == o); };
+
protected:
int32_t mAction;
int32_t mActionButton;
diff --git a/libs/input/Input.cpp b/libs/input/Input.cpp
index 1c713f9163..9e0ce1db33 100644
--- a/libs/input/Input.cpp
+++ b/libs/input/Input.cpp
@@ -1007,6 +1007,33 @@ PointerCoords MotionEvent::calculateTransformedCoords(uint32_t source,
return out;
}
+bool MotionEvent::operator==(const android::MotionEvent& o) const {
+ // We use NaN values to represent invalid cursor positions. Since NaN values are not equal
+ // to themselves according to IEEE 754, we cannot use the default equality operator to compare
+ // MotionEvents. Therefore we define a custom equality operator with special handling for NaNs.
+ // clang-format off
+ return InputEvent::operator==(static_cast<const InputEvent&>(o)) &&
+ mAction == o.mAction &&
+ mActionButton == o.mActionButton &&
+ mFlags == o.mFlags &&
+ mEdgeFlags == o.mEdgeFlags &&
+ mMetaState == o.mMetaState &&
+ mButtonState == o.mButtonState &&
+ mClassification == o.mClassification &&
+ mTransform == o.mTransform &&
+ mXPrecision == o.mXPrecision &&
+ mYPrecision == o.mYPrecision &&
+ ((std::isnan(mRawXCursorPosition) && std::isnan(o.mRawXCursorPosition)) ||
+ mRawXCursorPosition == o.mRawXCursorPosition) &&
+ ((std::isnan(mRawYCursorPosition) && std::isnan(o.mRawYCursorPosition)) ||
+ mRawYCursorPosition == o.mRawYCursorPosition) &&
+ mRawTransform == o.mRawTransform && mDownTime == o.mDownTime &&
+ mPointerProperties == o.mPointerProperties &&
+ mSampleEventTimes == o.mSampleEventTimes &&
+ mSamplePointerCoords == o.mSamplePointerCoords;
+ // clang-format on
+}
+
std::ostream& operator<<(std::ostream& out, const MotionEvent& event) {
out << "MotionEvent { action=" << MotionEvent::actionToString(event.getAction());
if (event.getActionButton() != 0) {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 8d43f3ba07..71afbccfab 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -365,17 +365,20 @@ size_t firstMarkedBit(T set) {
return i;
}
-std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarget,
+std::unique_ptr<DispatchEntry> createDispatchEntry(const IdGenerator& idGenerator,
+ const InputTarget& inputTarget,
std::shared_ptr<const EventEntry> eventEntry,
ftl::Flags<InputTarget::Flags> inputTargetFlags,
int64_t vsyncId) {
+ const bool zeroCoords = inputTargetFlags.test(InputTarget::Flags::ZERO_COORDS);
const sp<WindowInfoHandle> win = inputTarget.windowHandle;
const std::optional<int32_t> windowId =
win ? std::make_optional(win->getInfo()->id) : std::nullopt;
// Assume the only targets that are not associated with a window are global monitors, and use
// the system UID for global monitors for tracing purposes.
const gui::Uid uid = win ? win->getInfo()->ownerUid : gui::Uid(AID_SYSTEM);
- if (inputTarget.useDefaultPointerTransform()) {
+
+ if (inputTarget.useDefaultPointerTransform() && !zeroCoords) {
const ui::Transform& transform = inputTarget.getDefaultPointerTransform();
return std::make_unique<DispatchEntry>(eventEntry, inputTargetFlags, transform,
inputTarget.displayTransform,
@@ -386,33 +389,39 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarge
ALOG_ASSERT(eventEntry->type == EventEntry::Type::MOTION);
const MotionEntry& motionEntry = static_cast<const MotionEntry&>(*eventEntry);
- std::vector<PointerCoords> pointerCoords;
- pointerCoords.resize(motionEntry.getPointerCount());
-
- // 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 transform for the normalized pointer.
- const ui::Transform& firstPointerTransform =
- inputTarget.pointerTransforms[firstMarkedBit(inputTarget.pointerIds)];
- ui::Transform inverseFirstTransform = firstPointerTransform.inverse();
-
- // Iterate through all pointers in the event to normalize against the first.
- for (uint32_t pointerIndex = 0; pointerIndex < motionEntry.getPointerCount(); pointerIndex++) {
- const PointerProperties& pointerProperties = motionEntry.pointerProperties[pointerIndex];
- uint32_t pointerId = uint32_t(pointerProperties.id);
- const ui::Transform& currTransform = inputTarget.pointerTransforms[pointerId];
+ std::vector<PointerCoords> pointerCoords{motionEntry.getPointerCount()};
- pointerCoords[pointerIndex].copyFrom(motionEntry.pointerCoords[pointerIndex]);
- // First, apply the current pointer's transform to update the coordinates into
- // window space.
- pointerCoords[pointerIndex].transform(currTransform);
- // Next, apply the inverse transform of the normalized coordinates so the
- // current coordinates are transformed into the normalized coordinate space.
- pointerCoords[pointerIndex].transform(inverseFirstTransform);
+ const ui::Transform* transform = &kIdentityTransform;
+ const ui::Transform* displayTransform = &kIdentityTransform;
+ if (zeroCoords) {
+ std::for_each(pointerCoords.begin(), pointerCoords.end(), [](auto& pc) { pc.clear(); });
+ } else {
+ // 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 transform for the normalized pointer.
+ transform =
+ &inputTarget.getTransformForPointer(firstMarkedBit(inputTarget.getPointerIds()));
+ const ui::Transform inverseTransform = transform->inverse();
+ displayTransform = &inputTarget.displayTransform;
+
+ // Iterate through all pointers in the event to normalize against the first.
+ for (size_t i = 0; i < motionEntry.getPointerCount(); i++) {
+ PointerCoords& newCoords = pointerCoords[i];
+ const auto pointerId = motionEntry.pointerProperties[i].id;
+ const ui::Transform& currTransform = inputTarget.getTransformForPointer(pointerId);
+
+ newCoords.copyFrom(motionEntry.pointerCoords[i]);
+ // First, apply the current pointer's transform to update the coordinates into
+ // window space.
+ newCoords.transform(currTransform);
+ // Next, apply the inverse transform of the normalized coordinates so the
+ // current coordinates are transformed into the normalized coordinate space.
+ newCoords.transform(inverseTransform);
+ }
}
std::unique_ptr<MotionEntry> combinedMotionEntry =
- std::make_unique<MotionEntry>(motionEntry.id, motionEntry.injectionState,
+ std::make_unique<MotionEntry>(idGenerator.nextId(), motionEntry.injectionState,
motionEntry.eventTime, motionEntry.deviceId,
motionEntry.source, motionEntry.displayId,
motionEntry.policyFlags, motionEntry.action,
@@ -426,7 +435,7 @@ std::unique_ptr<DispatchEntry> createDispatchEntry(const InputTarget& inputTarge
std::unique_ptr<DispatchEntry> dispatchEntry =
std::make_unique<DispatchEntry>(std::move(combinedMotionEntry), inputTargetFlags,
- firstPointerTransform, inputTarget.displayTransform,
+ *transform, *displayTransform,
inputTarget.globalScaleFactor, uid, vsyncId, windowId);
return dispatchEntry;
}
@@ -1630,14 +1639,12 @@ void InputDispatcher::dispatchFocusLocked(nsecs_t currentTime,
if (connection == nullptr) {
return; // Connection has gone away
}
- InputTarget target;
- target.connection = connection;
entry->dispatchInProgress = true;
std::string message = std::string("Focus ") + (entry->hasFocus ? "entering " : "leaving ") +
connection->getInputChannelName();
std::string reason = std::string("reason=").append(entry->reason);
android_log_event_list(LOGTAG_INPUT_FOCUS) << message << reason << LOG_ID_EVENTS;
- dispatchEventLocked(currentTime, entry, {target});
+ dispatchEventLocked(currentTime, entry, {{connection}});
}
void InputDispatcher::dispatchPointerCaptureChangedLocked(
@@ -1703,10 +1710,8 @@ void InputDispatcher::dispatchPointerCaptureChangedLocked(
}
return;
}
- InputTarget target;
- target.connection = connection;
entry->dispatchInProgress = true;
- dispatchEventLocked(currentTime, entry, {target});
+ dispatchEventLocked(currentTime, entry, {{connection}});
dropReason = DropReason::NOT_DROPPED;
}
@@ -1739,9 +1744,7 @@ std::vector<InputTarget> InputDispatcher::getInputTargetsFromWindowHandlesLocked
if (connection == nullptr) {
continue; // Connection has gone away
}
- InputTarget target;
- target.connection = connection;
- inputTargets.push_back(target);
+ inputTargets.emplace_back(connection);
}
return inputTargets;
}
@@ -2022,10 +2025,8 @@ void InputDispatcher::dispatchDragLocked(nsecs_t currentTime,
if (connection == nullptr) {
return; // Connection has gone away
}
- InputTarget target;
- target.connection = connection;
entry->dispatchInProgress = true;
- dispatchEventLocked(currentTime, entry, {target});
+ dispatchEventLocked(currentTime, entry, {{connection}});
}
void InputDispatcher::logOutboundMotionDetails(const char* prefix, const MotionEntry& entry) {
@@ -2868,8 +2869,7 @@ std::optional<InputTarget> InputDispatcher::createInputTargetLocked(
ALOGW("Not creating InputTarget for %s, no input channel", windowHandle->getName().c_str());
return {};
}
- InputTarget inputTarget;
- inputTarget.connection = connection;
+ InputTarget inputTarget{connection};
inputTarget.windowHandle = windowHandle;
inputTarget.dispatchMode = dispatchMode;
inputTarget.flags = targetFlags;
@@ -2982,8 +2982,7 @@ void InputDispatcher::addGlobalMonitoringTargetsLocked(std::vector<InputTarget>&
if (monitorsIt == mGlobalMonitorsByDisplay.end()) return;
for (const Monitor& monitor : selectResponsiveMonitorsLocked(monitorsIt->second)) {
- InputTarget target;
- target.connection = monitor.connection;
+ InputTarget target{monitor.connection};
// target.firstDownTimeInTarget is not set for global monitors. It is only required in split
// touch and global monitoring works as intended even without setting firstDownTimeInTarget
if (const auto& it = mDisplayInfos.find(displayId); it != mDisplayInfos.end()) {
@@ -3275,7 +3274,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
ALOGD("channel '%s' ~ prepareDispatchCycle - flags=%s, "
"globalScaleFactor=%f, pointerIds=%s %s",
connection->getInputChannelName().c_str(), inputTarget.flags.string().c_str(),
- inputTarget.globalScaleFactor, bitsetToString(inputTarget.pointerIds).c_str(),
+ inputTarget.globalScaleFactor, bitsetToString(inputTarget.getPointerIds()).c_str(),
inputTarget.getPointerInfoString().c_str());
}
@@ -3297,7 +3296,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
ftl::enum_string(eventEntry->type).c_str());
const MotionEntry& originalMotionEntry = static_cast<const MotionEntry&>(*eventEntry);
- if (inputTarget.pointerIds.count() != originalMotionEntry.getPointerCount()) {
+ if (inputTarget.getPointerIds().count() != originalMotionEntry.getPointerCount()) {
if (!inputTarget.firstDownTimeInTarget.has_value()) {
logDispatchStateLocked();
LOG(FATAL) << "Splitting motion events requires a down time to be set for the "
@@ -3306,7 +3305,7 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime,
<< originalMotionEntry.getDescription();
}
std::unique_ptr<MotionEntry> splitMotionEntry =
- splitMotionEvent(originalMotionEntry, inputTarget.pointerIds,
+ splitMotionEvent(originalMotionEntry, inputTarget.getPointerIds(),
inputTarget.firstDownTimeInTarget.value());
if (!splitMotionEntry) {
return; // split event was dropped
@@ -3364,7 +3363,8 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio
// This is a new event.
// Enqueue a new dispatch entry onto the outbound queue for this connection.
std::unique_ptr<DispatchEntry> dispatchEntry =
- createDispatchEntry(inputTarget, eventEntry, inputTarget.flags, mWindowInfosVsyncId);
+ createDispatchEntry(mIdGenerator, inputTarget, eventEntry, inputTarget.flags,
+ mWindowInfosVsyncId);
// Use the eventEntry from dispatchEntry since the entry may have changed and can now be a
// different EventEntry than what was passed in.
@@ -3485,7 +3485,7 @@ void InputDispatcher::enqueueDispatchEntryLocked(const std::shared_ptr<Connectio
<< connection->getInputChannelName() << " with event "
<< cancelEvent->getDescription();
std::unique_ptr<DispatchEntry> cancelDispatchEntry =
- createDispatchEntry(inputTarget, std::move(cancelEvent),
+ createDispatchEntry(mIdGenerator, inputTarget, std::move(cancelEvent),
ftl::Flags<InputTarget::Flags>(), mWindowInfosVsyncId);
// Send these cancel events to the queue before sending the event from the new
@@ -3664,12 +3664,6 @@ status_t InputDispatcher::publishMotionEvent(Connection& connection,
}
usingCoords = scaledCoords;
}
- } else if (dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS)) {
- // We don't want the dispatch target to know the coordinates
- for (uint32_t i = 0; i < motionEntry.getPointerCount(); i++) {
- scaledCoords[i].clear();
- }
- usingCoords = scaledCoords;
}
std::array<uint8_t, 32> hmac = getSignature(motionEntry, dispatchEntry);
@@ -4109,7 +4103,7 @@ void InputDispatcher::synthesizeCancelationEventsForConnectionLocked(
const bool wasEmpty = connection->outboundQueue.empty();
// The target to use if we don't find a window associated with the channel.
- const InputTarget fallbackTarget{.connection = connection};
+ const InputTarget fallbackTarget{connection};
const auto& token = connection->getToken();
for (size_t i = 0; i < cancelationEvents.size(); i++) {
@@ -4227,8 +4221,7 @@ void InputDispatcher::synthesizePointerDownEventsForConnectionLocked(
targetFlags, pointerIds, motionEntry.downTime,
targets);
} else {
- targets.emplace_back(
- InputTarget{.connection = connection, .flags = targetFlags});
+ targets.emplace_back(connection, targetFlags);
const auto it = mDisplayInfos.find(motionEntry.displayId);
if (it != mDisplayInfos.end()) {
targets.back().displayTransform = it->second.transform;
diff --git a/services/inputflinger/dispatcher/InputTarget.cpp b/services/inputflinger/dispatcher/InputTarget.cpp
index 28e3c6d69a..35ad858736 100644
--- a/services/inputflinger/dispatcher/InputTarget.cpp
+++ b/services/inputflinger/dispatcher/InputTarget.cpp
@@ -26,6 +26,15 @@ using android::base::StringPrintf;
namespace android::inputdispatcher {
+namespace {
+
+const static ui::Transform kIdentityTransform{};
+
+}
+
+InputTarget::InputTarget(const std::shared_ptr<Connection>& connection, ftl::Flags<Flags> flags)
+ : connection(connection), flags(flags) {}
+
void InputTarget::addPointers(std::bitset<MAX_POINTER_ID + 1> newPointerIds,
const ui::Transform& transform) {
// The pointerIds can be empty, but still a valid InputTarget. This can happen when there is no
@@ -36,31 +45,46 @@ void InputTarget::addPointers(std::bitset<MAX_POINTER_ID + 1> newPointerIds,
}
// Ensure that the new set of pointers doesn't overlap with the current set of pointers.
- if ((pointerIds & newPointerIds).any()) {
+ if ((getPointerIds() & newPointerIds).any()) {
LOG(FATAL) << __func__ << " - overlap with incoming pointers "
<< bitsetToString(newPointerIds) << " in " << *this;
}
- pointerIds |= newPointerIds;
- for (size_t i = 0; i < newPointerIds.size(); i++) {
- if (!newPointerIds.test(i)) {
- continue;
+ for (auto& [existingTransform, existingPointers] : mPointerTransforms) {
+ if (transform == existingTransform) {
+ existingPointers |= newPointerIds;
+ return;
}
- pointerTransforms[i] = transform;
}
+ mPointerTransforms.emplace_back(transform, newPointerIds);
}
void InputTarget::setDefaultPointerTransform(const ui::Transform& transform) {
- pointerIds.reset();
- pointerTransforms[0] = transform;
+ mPointerTransforms = {{transform, {}}};
}
bool InputTarget::useDefaultPointerTransform() const {
- return pointerIds.none();
+ return mPointerTransforms.size() <= 1;
}
const ui::Transform& InputTarget::getDefaultPointerTransform() const {
- return pointerTransforms[0];
+ if (!useDefaultPointerTransform()) {
+ LOG(FATAL) << __func__ << ": Not using default pointer transform";
+ }
+ return mPointerTransforms.size() == 1 ? mPointerTransforms[0].first : kIdentityTransform;
+}
+
+const ui::Transform& InputTarget::getTransformForPointer(int32_t pointerId) const {
+ for (const auto& [transform, ids] : mPointerTransforms) {
+ if (ids.test(pointerId)) {
+ return transform;
+ }
+ }
+
+ LOG(FATAL) << __func__
+ << ": Cannot get transform: The following Pointer ID does not exist in target: "
+ << pointerId;
+ return kIdentityTransform;
}
std::string InputTarget::getPointerInfoString() const {
@@ -71,17 +95,21 @@ std::string InputTarget::getPointerInfoString() const {
return out;
}
- for (uint32_t i = 0; i < pointerIds.size(); i++) {
- if (!pointerIds.test(i)) {
- continue;
- }
-
- const std::string name = "pointerId " + std::to_string(i) + ":";
- pointerTransforms[i].dump(out, name.c_str(), " ");
+ for (const auto& [transform, ids] : mPointerTransforms) {
+ const std::string name = "pointerIds " + bitsetToString(ids) + ":";
+ transform.dump(out, name.c_str(), " ");
}
return out;
}
+std::bitset<MAX_POINTER_ID + 1> InputTarget::getPointerIds() const {
+ PointerIds allIds;
+ for (const auto& [_, ids] : mPointerTransforms) {
+ allIds |= ids;
+ }
+ return allIds;
+}
+
std::ostream& operator<<(std::ostream& out, const InputTarget& target) {
out << "{connection=";
if (target.connection != nullptr) {
diff --git a/services/inputflinger/dispatcher/InputTarget.h b/services/inputflinger/dispatcher/InputTarget.h
index 5728bdf1e8..058639a742 100644
--- a/services/inputflinger/dispatcher/InputTarget.h
+++ b/services/inputflinger/dispatcher/InputTarget.h
@@ -33,7 +33,8 @@ namespace android::inputdispatcher {
* be added to input event coordinates to compensate for the absolute position of the
* window area.
*/
-struct InputTarget {
+class InputTarget {
+public:
using Flags = InputTargetFlags;
enum class DispatchMode {
@@ -80,20 +81,17 @@ struct InputTarget {
// Current display transform. Used for compatibility for raw coordinates.
ui::Transform displayTransform;
- // The subset of pointer ids to include in motion events dispatched to this input target
- // if FLAG_SPLIT is set.
- std::bitset<MAX_POINTER_ID + 1> pointerIds;
// Event time for the first motion event (ACTION_DOWN) dispatched to this input target if
// FLAG_SPLIT is set.
std::optional<nsecs_t> firstDownTimeInTarget;
- // The data is stored by the pointerId. Use the bit position of pointerIds to look up
- // Transform per pointerId.
- ui::Transform pointerTransforms[MAX_POINTERS];
// The window that this input target is being dispatched to. It is possible for this to be
// null for cases like global monitors.
sp<gui::WindowInfoHandle> windowHandle;
+ InputTarget() = default;
+ InputTarget(const std::shared_ptr<Connection>&, ftl::Flags<Flags> = {});
+
void addPointers(std::bitset<MAX_POINTER_ID + 1> pointerIds, const ui::Transform& transform);
void setDefaultPointerTransform(const ui::Transform& transform);
@@ -111,7 +109,22 @@ struct InputTarget {
*/
const ui::Transform& getDefaultPointerTransform() const;
+ const ui::Transform& getTransformForPointer(int32_t pointerId) const;
+
+ std::bitset<MAX_POINTER_ID + 1> getPointerIds() const;
+
std::string getPointerInfoString() const;
+
+private:
+ template <typename K, typename V>
+ using ArrayMap = std::vector<std::pair<K, V>>;
+ using PointerIds = std::bitset<MAX_POINTER_ID + 1>;
+ // The mapping of pointer IDs to the transform that should be used for that collection of IDs.
+ // Each of the pointer IDs are mutually disjoint, and their union makes up pointer IDs to
+ // include in the motion events dispatched to this target. We use an ArrayMap to store this to
+ // avoid having to define hash or comparison functions for ui::Transform, which would be needed
+ // to use std::unordered_map or std::map respectively.
+ ArrayMap<ui::Transform, PointerIds> mPointerTransforms;
};
std::ostream& operator<<(std::ostream& out, const InputTarget& target);
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.cpp b/services/inputflinger/tests/FakeInputTracingBackend.cpp
index f4a06f7d28..b7af356eba 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.cpp
+++ b/services/inputflinger/tests/FakeInputTracingBackend.cpp
@@ -37,6 +37,30 @@ inline auto getId(const trace::TracedEvent& v) {
return std::visit([](const auto& event) { return event.id; }, v);
}
+MotionEvent toInputEvent(
+ const trace::TracedMotionEvent& e,
+ const trace::InputTracingBackendInterface::WindowDispatchArgs& dispatchArgs,
+ const std::array<uint8_t, 32>& hmac) {
+ MotionEvent traced;
+ traced.initialize(e.id, e.deviceId, e.source, e.displayId, hmac, e.action, e.actionButton,
+ dispatchArgs.resolvedFlags, e.edgeFlags, e.metaState, e.buttonState,
+ e.classification, dispatchArgs.transform, e.xPrecision, e.yPrecision,
+ e.xCursorPosition, e.yCursorPosition, dispatchArgs.rawTransform, e.downTime,
+ e.eventTime, e.pointerProperties.size(), e.pointerProperties.data(),
+ e.pointerCoords.data());
+ return traced;
+}
+
+KeyEvent toInputEvent(const trace::TracedKeyEvent& e,
+ const trace::InputTracingBackendInterface::WindowDispatchArgs& dispatchArgs,
+ const std::array<uint8_t, 32>& hmac) {
+ KeyEvent traced;
+ traced.initialize(e.id, e.deviceId, e.source, e.displayId, hmac, e.action,
+ dispatchArgs.resolvedFlags, e.keyCode, e.scanCode, e.metaState, e.repeatCount,
+ e.downTime, e.eventTime);
+ return traced;
+}
+
} // namespace
// --- VerifyingTrace ---
@@ -55,6 +79,7 @@ void VerifyingTrace::verifyExpectedEventsTraced() {
std::unique_lock lock(mLock);
base::ScopedLockAssertion assumeLocked(mLock);
+ // Poll for all expected events to be traced, and keep track of the latest poll result.
base::Result<void> result;
mEventTracedCondition.wait_for(lock, TRACE_TIMEOUT, [&]() REQUIRES(mLock) {
for (const auto& [expectedEvent, windowId] : mExpectedEvents) {
@@ -101,12 +126,39 @@ base::Result<void> VerifyingTrace::verifyEventTraced(const Event& expectedEvent,
});
if (tracedDispatchesIt == mTracedWindowDispatches.end()) {
msg << "Expected dispatch of event with ID 0x" << std::hex << expectedEvent.getId()
- << " to window with ID 0x" << expectedWindowId << " to be traced, but it was not."
- << "\nExpected event: " << expectedEvent;
+ << " to window with ID 0x" << expectedWindowId << " to be traced, but it was not.\n"
+ << "Expected event: " << expectedEvent;
return error(msg);
}
- return {};
+ // Verify that the traced event matches the expected event exactly.
+ return std::visit(
+ [&](const auto& traced) -> base::Result<void> {
+ Event tracedEvent;
+ using T = std::decay_t<decltype(traced)>;
+ if constexpr (std::is_same_v<Event, MotionEvent> &&
+ std::is_same_v<T, trace::TracedMotionEvent>) {
+ tracedEvent =
+ toInputEvent(traced, *tracedDispatchesIt, expectedEvent.getHmac());
+ } else if constexpr (std::is_same_v<Event, KeyEvent> &&
+ std::is_same_v<T, trace::TracedKeyEvent>) {
+ tracedEvent =
+ toInputEvent(traced, *tracedDispatchesIt, expectedEvent.getHmac());
+ } else {
+ msg << "Received the wrong event type!\n"
+ << "Expected event: " << expectedEvent;
+ return error(msg);
+ }
+
+ const auto result = testing::internal::CmpHelperEQ("expectedEvent", "tracedEvent",
+ expectedEvent, tracedEvent);
+ if (!result) {
+ msg << result.failure_message();
+ return error(msg);
+ }
+ return {};
+ },
+ tracedEventsIt->second);
}
// --- FakeInputTracingBackend ---
@@ -114,7 +166,7 @@ base::Result<void> VerifyingTrace::verifyEventTraced(const Event& expectedEvent,
void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event) const {
{
std::scoped_lock lock(mTrace->mLock);
- mTrace->mTracedEvents.emplace(event.id);
+ mTrace->mTracedEvents.emplace(event.id, event);
}
mTrace->mEventTracedCondition.notify_all();
}
@@ -122,7 +174,7 @@ void FakeInputTracingBackend::traceKeyEvent(const trace::TracedKeyEvent& event)
void FakeInputTracingBackend::traceMotionEvent(const trace::TracedMotionEvent& event) const {
{
std::scoped_lock lock(mTrace->mLock);
- mTrace->mTracedEvents.emplace(event.id);
+ mTrace->mTracedEvents.emplace(event.id, event);
}
mTrace->mEventTracedCondition.notify_all();
}
diff --git a/services/inputflinger/tests/FakeInputTracingBackend.h b/services/inputflinger/tests/FakeInputTracingBackend.h
index 40ca3a29f8..108419aaaf 100644
--- a/services/inputflinger/tests/FakeInputTracingBackend.h
+++ b/services/inputflinger/tests/FakeInputTracingBackend.h
@@ -25,7 +25,7 @@
#include <condition_variable>
#include <memory>
#include <mutex>
-#include <unordered_set>
+#include <unordered_map>
#include <vector>
namespace android::inputdispatcher {
@@ -58,7 +58,7 @@ public:
private:
std::mutex mLock;
std::condition_variable mEventTracedCondition;
- std::unordered_set<uint32_t /*eventId*/> mTracedEvents GUARDED_BY(mLock);
+ std::unordered_map<uint32_t /*eventId*/, trace::TracedEvent> mTracedEvents GUARDED_BY(mLock);
using WindowDispatchArgs = trace::InputTracingBackendInterface::WindowDispatchArgs;
std::vector<WindowDispatchArgs> mTracedWindowDispatches GUARDED_BY(mLock);
std::vector<std::pair<std::variant<KeyEvent, MotionEvent>, int32_t /*windowId*/>>
diff --git a/services/inputflinger/tests/InputDispatcher_test.cpp b/services/inputflinger/tests/InputDispatcher_test.cpp
index ceb3c41294..f0f4d93ecd 100644
--- a/services/inputflinger/tests/InputDispatcher_test.cpp
+++ b/services/inputflinger/tests/InputDispatcher_test.cpp
@@ -3865,48 +3865,57 @@ TEST_F(InputDispatcherTest, SplitTouchesSendCorrectActionDownTime) {
// Touch down on the first window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_DOWN, {{50, 50}}));
-
mDispatcher->waitForIdle();
- std::unique_ptr<MotionEvent> motionEvent1 = window1->consumeMotionEvent();
- ASSERT_NE(nullptr, motionEvent1);
+ const std::unique_ptr<MotionEvent> firstDown =
+ window1->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
+ ASSERT_EQ(firstDown->getDownTime(), firstDown->getEventTime());
window2->assertNoEvents();
- nsecs_t downTimeForWindow1 = motionEvent1->getDownTime();
- ASSERT_EQ(motionEvent1->getDownTime(), motionEvent1->getEventTime());
// Now touch down on the window with another pointer
mDispatcher->notifyMotion(generateTouchArgs(POINTER_1_DOWN, {{50, 50}, {150, 50}}));
mDispatcher->waitForIdle();
- std::unique_ptr<MotionEvent> motionEvent2 = window2->consumeMotionEvent();
- ASSERT_NE(nullptr, motionEvent2);
- nsecs_t downTimeForWindow2 = motionEvent2->getDownTime();
- ASSERT_NE(downTimeForWindow1, downTimeForWindow2);
- ASSERT_EQ(motionEvent2->getDownTime(), motionEvent2->getEventTime());
+
+ const std::unique_ptr<MotionEvent> secondDown =
+ window2->consumeMotionEvent(AllOf(WithMotionAction(ACTION_DOWN)));
+ ASSERT_EQ(secondDown->getDownTime(), secondDown->getEventTime());
+ ASSERT_NE(firstDown->getDownTime(), secondDown->getDownTime());
+ // We currently send MOVE events to all windows receiving a split touch when there is any change
+ // in the touch state, even when none of the pointers in the split window actually moved.
+ // Document this behavior in the test.
+ window1->consumeMotionMove();
// Now move the pointer on the second window
mDispatcher->notifyMotion(generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{50, 50}, {151, 51}}));
mDispatcher->waitForIdle();
- window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
+
+ window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime()));
+ window1->consumeMotionEvent(WithDownTime(firstDown->getDownTime()));
// Now add new touch down on the second window
mDispatcher->notifyMotion(generateTouchArgs(POINTER_2_DOWN, {{50, 50}, {151, 51}, {150, 50}}));
mDispatcher->waitForIdle();
- window2->consumeMotionEvent(WithDownTime(downTimeForWindow2));
- // TODO(b/232530217): do not send the unnecessary MOVE event and delete the next line
- window1->consumeMotionMove();
- window1->assertNoEvents();
+ window2->consumeMotionEvent(
+ AllOf(WithMotionAction(POINTER_1_DOWN), WithDownTime(secondDown->getDownTime())));
+ window1->consumeMotionEvent(WithDownTime(firstDown->getDownTime()));
// Now move the pointer on the first window
mDispatcher->notifyMotion(
generateTouchArgs(AMOTION_EVENT_ACTION_MOVE, {{51, 51}, {151, 51}, {150, 50}}));
mDispatcher->waitForIdle();
- window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
+ window1->consumeMotionEvent(WithDownTime(firstDown->getDownTime()));
+ window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime()));
+
+ // Now add new touch down on the first window
mDispatcher->notifyMotion(
generateTouchArgs(POINTER_3_DOWN, {{51, 51}, {151, 51}, {150, 50}, {50, 50}}));
mDispatcher->waitForIdle();
- window1->consumeMotionEvent(WithDownTime(downTimeForWindow1));
+
+ window1->consumeMotionEvent(
+ AllOf(WithMotionAction(POINTER_1_DOWN), WithDownTime(firstDown->getDownTime())));
+ window2->consumeMotionEvent(WithDownTime(secondDown->getDownTime()));
}
TEST_F(InputDispatcherTest, HoverMoveEnterMouseClickAndHoverMoveExit) {
@@ -10799,6 +10808,7 @@ TEST_F(InputDispatcherDragTests, DragAndDropWhenSplitTouch) {
InputEventInjectionSync::WAIT_FOR_RESULT))
<< "Inject motion event should return InputEventInjectionResult::SUCCEEDED";
mWindow->consumeMotionDown(ADISPLAY_ID_DEFAULT);
+ mSecondWindow->consumeMotionMove(ADISPLAY_ID_DEFAULT);
// Perform drag and drop from first window.
ASSERT_TRUE(startDrag(false));