diff options
author | Prabir Pradhan <prabirmsp@google.com> | 2024-02-22 03:14:17 +0000 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2024-02-22 03:14:17 +0000 |
commit | 7438201514d9a11995dff49c6af3dd3045b08098 (patch) | |
tree | bb794cecc2e6852dc5de1f3b8984b8fed9d9f035 | |
parent | 77add0b0f163a11792da4e2f0e694ed34e87589f (diff) | |
parent | 65a071a788a1f5e575d85460d08182d0726abc83 (diff) | |
download | native-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.h | 10 | ||||
-rw-r--r-- | libs/input/Input.cpp | 27 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputDispatcher.cpp | 105 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputTarget.cpp | 62 | ||||
-rw-r--r-- | services/inputflinger/dispatcher/InputTarget.h | 27 | ||||
-rw-r--r-- | services/inputflinger/tests/FakeInputTracingBackend.cpp | 62 | ||||
-rw-r--r-- | services/inputflinger/tests/FakeInputTracingBackend.h | 4 | ||||
-rw-r--r-- | services/inputflinger/tests/InputDispatcher_test.cpp | 44 |
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)); |