Query InputState for pointer information when canceling a gesture
When the dispatcher produces a synthetic HOVER_EXIT or ACTION_CANCEL, it
relies on the InputState to get the correct coordinates of this event.
That's because these events should use the same coordinates as the last
dispatched HOVER_MOVE or ACTION_MOVE event, and this data is stored
inside InputState.
Before this CL, this logic was checked by "HoverIntoClone" test. When
this test ran, the dispatcher produced scary messages, saying that it
was canceling events because an inconsistent event was received. Another
test that had these logs was StylusHoverAndDownNoInputChannel. This
could also be easily reproduced by sending a HOVER_ENTER and then a
HOVER_EXIT. The HOVER_EXIT event would get rejected, and the dispatcher
would use InputState to generate its own.
Now with this CL, these messages are gone because the dispatcher is
querying for these coords proactively instead of using the cancellation
mechanism of InputState.
Bug: 317183325
Test: TEST=inputflinger_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="*StylusHoverAndDownNoInputChannel"
Change-Id: I44a2446171ab5d2368d7b7f8d806e45d5bb855c0
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index 8d0ff4b..fa02a33 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3389,27 +3389,40 @@
dispatchEntry->resolvedFlags = resolvedFlags;
if (resolvedAction != motionEntry.action) {
+ std::optional<std::vector<PointerProperties>> usingProperties;
+ std::optional<std::vector<PointerCoords>> usingCoords;
+ if (resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT ||
+ resolvedAction == AMOTION_EVENT_ACTION_CANCEL) {
+ // This is a HOVER_EXIT or an ACTION_CANCEL event that was synthesized by
+ // the dispatcher, and therefore the coordinates of this event are currently
+ // incorrect. These events should use the coordinates of the last dispatched
+ // ACTION_MOVE or HOVER_MOVE. We need to query InputState to get this data.
+ const bool hovering = resolvedAction == AMOTION_EVENT_ACTION_HOVER_EXIT;
+ std::optional<std::pair<std::vector<PointerProperties>,
+ std::vector<PointerCoords>>>
+ pointerInfo =
+ connection->inputState.getPointersOfLastEvent(motionEntry,
+ hovering);
+ if (pointerInfo) {
+ usingProperties = pointerInfo->first;
+ usingCoords = pointerInfo->second;
+ }
+ }
// Generate a new MotionEntry with a new eventId using the resolved action and
// flags.
- resolvedMotion =
- std::make_shared<MotionEntry>(mIdGenerator.nextId(),
- motionEntry.injectionState,
- motionEntry.eventTime,
- motionEntry.deviceId, motionEntry.source,
- motionEntry.displayId,
- motionEntry.policyFlags, resolvedAction,
- motionEntry.actionButton, resolvedFlags,
- motionEntry.metaState,
- motionEntry.buttonState,
- motionEntry.classification,
- motionEntry.edgeFlags,
- motionEntry.xPrecision,
- motionEntry.yPrecision,
- motionEntry.xCursorPosition,
- motionEntry.yCursorPosition,
- motionEntry.downTime,
- motionEntry.pointerProperties,
- motionEntry.pointerCoords);
+ resolvedMotion = std::make_shared<
+ MotionEntry>(mIdGenerator.nextId(), motionEntry.injectionState,
+ motionEntry.eventTime, motionEntry.deviceId,
+ motionEntry.source, motionEntry.displayId,
+ motionEntry.policyFlags, resolvedAction,
+ motionEntry.actionButton, resolvedFlags,
+ motionEntry.metaState, motionEntry.buttonState,
+ motionEntry.classification, motionEntry.edgeFlags,
+ motionEntry.xPrecision, motionEntry.yPrecision,
+ motionEntry.xCursorPosition, motionEntry.yCursorPosition,
+ motionEntry.downTime,
+ usingProperties.value_or(motionEntry.pointerProperties),
+ usingCoords.value_or(motionEntry.pointerCoords));
if (ATRACE_ENABLED()) {
std::string message = StringPrintf("Transmute MotionEvent(id=0x%" PRIx32
") to MotionEvent(id=0x%" PRIx32 ").",
diff --git a/services/inputflinger/dispatcher/InputState.cpp b/services/inputflinger/dispatcher/InputState.cpp
index a4ac4fb..1fec9b7 100644
--- a/services/inputflinger/dispatcher/InputState.cpp
+++ b/services/inputflinger/dispatcher/InputState.cpp
@@ -195,6 +195,16 @@
}
}
+std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>>
+InputState::getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const {
+ ssize_t index = findMotionMemento(entry, hovering);
+ if (index == -1) {
+ return std::nullopt;
+ }
+ return std::make_pair(mMotionMementos[index].pointerProperties,
+ mMotionMementos[index].pointerCoords);
+}
+
ssize_t InputState::findKeyMemento(const KeyEntry& entry) const {
for (size_t i = 0; i < mKeyMementos.size(); i++) {
const KeyMemento& memento = mKeyMementos[i];
@@ -311,16 +321,6 @@
return true;
}
- // Use the previous stream cancellation logic to generate all HOVER_EXIT events.
- // If this hover event was generated as a result of the pointer leaving the window,
- // the HOVER_EXIT event should have the same coordinates as the previous
- // HOVER_MOVE event in this stream. Ensure that all HOVER_EXITs have the same
- // coordinates as the previous event by cancelling the stream here. With this approach, the
- // HOVER_EXIT event is generated from the previous event.
- if (actionMasked == AMOTION_EVENT_ACTION_HOVER_EXIT && lastMemento.hovering) {
- return true;
- }
-
// If the stream changes its source, just cancel the current gesture to be safe. It's
// possible that the app isn't handling source changes properly
if (motionEntry.source != lastMemento.source) {
diff --git a/services/inputflinger/dispatcher/InputState.h b/services/inputflinger/dispatcher/InputState.h
index b0e4209..d49469d 100644
--- a/services/inputflinger/dispatcher/InputState.h
+++ b/services/inputflinger/dispatcher/InputState.h
@@ -48,6 +48,15 @@
// and should be skipped.
bool trackMotion(const MotionEntry& entry, int32_t flags);
+ /**
+ * Return the PointerProperties and the PointerCoords for the last event, if found. Return
+ * std::nullopt if not found. We should not return std::vector<PointerCoords> in isolation,
+ * because the pointers can technically be stored in the vector in any order, so the
+ * PointerProperties are needed to specify the order in which the pointer coords are stored.
+ */
+ std::optional<std::pair<std::vector<PointerProperties>, std::vector<PointerCoords>>>
+ getPointersOfLastEvent(const MotionEntry& entry, bool hovering) const;
+
// Create cancel events for the previous stream if the current motionEntry requires it.
std::unique_ptr<EventEntry> cancelConflictingInputStream(const MotionEntry& motionEntry);