Reset fingers and buttons in GestureConverter::reset
Previously, calling reset didn't really reset anything, which could have
resulted in an inconsistent event stream (e.g. fake fingers that were
put down but never raised).
Bug: 251196347
Bug: 259547750
Test: atest inputflinger_tests
Test: scroll, swipe, and pinch on a touchpad, check dispatched events
Change-Id: If95cde96f1974bf7c98009c68e2637686bff96cc
diff --git a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
index 4c56b05..5e63e48 100644
--- a/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchpadInputMapper.cpp
@@ -256,8 +256,9 @@
std::list<NotifyArgs> TouchpadInputMapper::reset(nsecs_t when) {
mStateConverter.reset();
- mGestureConverter.reset();
- return InputMapper::reset(when);
+ std::list<NotifyArgs> out = mGestureConverter.reset(when);
+ out += InputMapper::reset(when);
+ return out;
}
std::list<NotifyArgs> TouchpadInputMapper::process(const RawEvent* rawEvent) {
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
index 707b1f3..fd2be5f 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.cpp
@@ -72,8 +72,32 @@
return out.str();
}
-void GestureConverter::reset() {
- mButtonState = 0;
+std::list<NotifyArgs> GestureConverter::reset(nsecs_t when) {
+ std::list<NotifyArgs> out;
+ switch (mCurrentClassification) {
+ case MotionClassification::TWO_FINGER_SWIPE:
+ out.push_back(endScroll(when, when));
+ break;
+ case MotionClassification::MULTI_FINGER_SWIPE:
+ out += handleMultiFingerSwipeLift(when, when);
+ break;
+ case MotionClassification::PINCH:
+ out += endPinch(when, when);
+ break;
+ case MotionClassification::NONE:
+ // When a button is pressed, the Gestures library always ends the current gesture,
+ // so we don't have to worry about the case where buttons need to be lifted during a
+ // pinch or swipe.
+ if (mButtonState) {
+ out += releaseAllButtons(when, when);
+ }
+ break;
+ default:
+ break;
+ }
+ mCurrentClassification = MotionClassification::NONE;
+ mDownTime = 0;
+ return out;
}
void GestureConverter::populateMotionRanges(InputDeviceInfo& info) const {
@@ -219,6 +243,39 @@
return out;
}
+std::list<NotifyArgs> GestureConverter::releaseAllButtons(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+
+ PointerCoords coords;
+ coords.clear();
+ coords.setAxisValue(AMOTION_EVENT_AXIS_X, xCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_X, 0);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_RELATIVE_Y, 0);
+ const bool pointerDown = isPointerDown(mButtonState);
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, pointerDown ? 1.0f : 0.0f);
+ uint32_t newButtonState = mButtonState;
+ for (uint32_t button = AMOTION_EVENT_BUTTON_PRIMARY; button <= AMOTION_EVENT_BUTTON_FORWARD;
+ button <<= 1) {
+ if (mButtonState & button) {
+ newButtonState &= ~button;
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_BUTTON_RELEASE,
+ button, newButtonState, /*pointerCount=*/1,
+ mFingerProps.data(), &coords, xCursorPosition,
+ yCursorPosition));
+ }
+ }
+ if (pointerDown) {
+ coords.setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 0.0f);
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
+ newButtonState, /*pointerCount=*/1, mFingerProps.data(),
+ &coords, xCursorPosition, yCursorPosition));
+ }
+ mButtonState = 0;
+ return out;
+}
+
std::list<NotifyArgs> GestureConverter::handleScroll(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
std::list<NotifyArgs> out;
@@ -264,6 +321,10 @@
return {};
}
+ return endScroll(when, readTime);
+}
+
+NotifyArgs GestureConverter::endScroll(nsecs_t when, nsecs_t readTime) {
const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE, 0);
mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE, 0);
@@ -366,8 +427,6 @@
[[nodiscard]] std::list<NotifyArgs> GestureConverter::handlePinch(nsecs_t when, nsecs_t readTime,
const Gesture& gesture) {
- std::list<NotifyArgs> out;
-
const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
// Pinch gesture phases are reported a little differently from others, in that the same details
@@ -391,6 +450,7 @@
mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_PRESSURE, 1.0f);
mDownTime = when;
+ std::list<NotifyArgs> out;
out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_DOWN,
/* actionButton= */ 0, mButtonState, /* pointerCount= */ 1,
mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
@@ -405,19 +465,7 @@
}
if (gesture.details.pinch.zoom_state == GESTURES_ZOOM_END) {
- mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
- out.push_back(makeMotionArgs(when, readTime,
- AMOTION_EVENT_ACTION_POINTER_UP |
- 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
- /* actionButton= */ 0, mButtonState, /* pointerCount= */ 2,
- mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
- yCursorPosition));
- out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /* actionButton= */ 0,
- mButtonState, /* pointerCount= */ 1, mFingerProps.data(),
- mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
- mCurrentClassification = MotionClassification::NONE;
- mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0);
- return out;
+ return endPinch(when, readTime);
}
mPinchFingerSeparation *= gesture.details.pinch.dz;
@@ -429,9 +477,27 @@
mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_X,
xCursorPosition + mPinchFingerSeparation / 2);
mFakeFingerCoords[1].setAxisValue(AMOTION_EVENT_AXIS_Y, yCursorPosition);
- out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /* actionButton= */ 0,
- mButtonState, /* pointerCount= */ 2, mFingerProps.data(),
+ return {makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_MOVE, /*actionButton=*/0,
+ mButtonState, /*pointerCount=*/2, mFingerProps.data(),
+ mFakeFingerCoords.data(), xCursorPosition, yCursorPosition)};
+}
+
+std::list<NotifyArgs> GestureConverter::endPinch(nsecs_t when, nsecs_t readTime) {
+ std::list<NotifyArgs> out;
+ const auto [xCursorPosition, yCursorPosition] = mPointerController->getPosition();
+
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 1.0);
+ out.push_back(makeMotionArgs(when, readTime,
+ AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT,
+ /*actionButton=*/0, mButtonState, /*pointerCount=*/2,
+ mFingerProps.data(), mFakeFingerCoords.data(), xCursorPosition,
+ yCursorPosition));
+ out.push_back(makeMotionArgs(when, readTime, AMOTION_EVENT_ACTION_UP, /*actionButton=*/0,
+ mButtonState, /*pointerCount=*/1, mFingerProps.data(),
mFakeFingerCoords.data(), xCursorPosition, yCursorPosition));
+ mCurrentClassification = MotionClassification::NONE;
+ mFakeFingerCoords[0].setAxisValue(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR, 0);
return out;
}
diff --git a/services/inputflinger/reader/mapper/gestures/GestureConverter.h b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
index a10dcce..2714d03 100644
--- a/services/inputflinger/reader/mapper/gestures/GestureConverter.h
+++ b/services/inputflinger/reader/mapper/gestures/GestureConverter.h
@@ -44,7 +44,7 @@
std::string dump() const;
void setOrientation(ui::Rotation orientation) { mOrientation = orientation; }
- void reset();
+ [[nodiscard]] std::list<NotifyArgs> reset(nsecs_t when);
void populateMotionRanges(InputDeviceInfo& info) const;
@@ -55,15 +55,19 @@
[[nodiscard]] NotifyArgs handleMove(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
[[nodiscard]] std::list<NotifyArgs> handleButtonsChange(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
+ [[nodiscard]] std::list<NotifyArgs> releaseAllButtons(nsecs_t when, nsecs_t readTime);
[[nodiscard]] std::list<NotifyArgs> handleScroll(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
[[nodiscard]] NotifyArgs handleFling(nsecs_t when, nsecs_t readTime, const Gesture& gesture);
+ [[nodiscard]] NotifyArgs endScroll(nsecs_t when, nsecs_t readTime);
+
[[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipe(nsecs_t when, nsecs_t readTime,
uint32_t fingerCount, float dx,
float dy);
[[nodiscard]] std::list<NotifyArgs> handleMultiFingerSwipeLift(nsecs_t when, nsecs_t readTime);
[[nodiscard]] std::list<NotifyArgs> handlePinch(nsecs_t when, nsecs_t readTime,
const Gesture& gesture);
+ [[nodiscard]] std::list<NotifyArgs> endPinch(nsecs_t when, nsecs_t readTime);
NotifyMotionArgs makeMotionArgs(nsecs_t when, nsecs_t readTime, int32_t action,
int32_t actionButton, int32_t buttonState,
diff --git a/services/inputflinger/tests/GestureConverter_test.cpp b/services/inputflinger/tests/GestureConverter_test.cpp
index bbf7e8e..33f404d 100644
--- a/services/inputflinger/tests/GestureConverter_test.cpp
+++ b/services/inputflinger/tests/GestureConverter_test.cpp
@@ -785,4 +785,107 @@
WithGesturePinchScaleFactor(0, EPSILON)));
}
+TEST_F(GestureConverterTest, ResetWithButtonPressed) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture downGesture(kGestureButtonsChange, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME,
+ /*down=*/GESTURES_BUTTON_LEFT | GESTURES_BUTTON_RIGHT,
+ /*up=*/GESTURES_BUTTON_NONE, /*is_tap=*/false);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, downGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_EQ(3u, args.size());
+
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_PRIMARY),
+ WithButtonState(AMOTION_EVENT_BUTTON_SECONDARY),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_BUTTON_RELEASE),
+ WithActionButton(AMOTION_EVENT_BUTTON_SECONDARY), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithButtonState(0),
+ WithCoords(POINTER_X, POINTER_Y),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
+TEST_F(GestureConverterTest, ResetDuringScroll) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture startGesture(kGestureScroll, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, 0, -10);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_EQ(1u, args.size());
+ ASSERT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithCoords(POINTER_X, POINTER_Y - 10),
+ WithGestureScrollDistance(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::TWO_FINGER_SWIPE),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER),
+ WithFlags(AMOTION_EVENT_FLAG_IS_GENERATED_GESTURE)));
+}
+
+TEST_F(GestureConverterTest, ResetDuringThreeFingerSwipe) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture startGesture(kGestureSwipe, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dx=*/0,
+ /*dy=*/10);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_EQ(3u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 2 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(3u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(2u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP), WithGestureOffset(0, 0, EPSILON),
+ WithMotionClassification(MotionClassification::MULTI_FINGER_SWIPE),
+ WithPointerCount(1u), WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
+TEST_F(GestureConverterTest, ResetDuringPinch) {
+ InputDeviceContext deviceContext(*mDevice, EVENTHUB_ID);
+ GestureConverter converter(*mReader->getContext(), deviceContext, DEVICE_ID);
+
+ Gesture startGesture(kGesturePinch, ARBITRARY_GESTURE_TIME, ARBITRARY_GESTURE_TIME, /*dz=*/1,
+ GESTURES_ZOOM_START);
+ (void)converter.handleGesture(ARBITRARY_TIME, READ_TIME, startGesture);
+
+ std::list<NotifyArgs> args = converter.reset(ARBITRARY_TIME);
+ ASSERT_EQ(2u, args.size());
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_POINTER_UP |
+ 1 << AMOTION_EVENT_ACTION_POINTER_INDEX_SHIFT),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(2u),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+ args.pop_front();
+ EXPECT_THAT(std::get<NotifyMotionArgs>(args.front()),
+ AllOf(WithMotionAction(AMOTION_EVENT_ACTION_UP),
+ WithMotionClassification(MotionClassification::PINCH),
+ WithGesturePinchScaleFactor(1.0f, EPSILON), WithPointerCount(1u),
+ WithToolType(AMOTION_EVENT_TOOL_TYPE_FINGER)));
+}
+
} // namespace android