Add check to not resample when resample time equals motion event time

Included SampleTimeEqualsEventTime from TouchResampling_test.cpp into
InputConsumerResampling_test.cpp, and added the missing logic in
LegacyResampler to pass the test.

Bug: 297226446
Flag: EXEMPT refactor
Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="InputConsumerResamplingTest*"
Change-Id: I8ff9a263ea79eed1b814d2b1ce0b7efb5ade584e
diff --git a/include/input/Resampler.h b/include/input/Resampler.h
index 4aaeddd..da0c5b2 100644
--- a/include/input/Resampler.h
+++ b/include/input/Resampler.h
@@ -65,7 +65,8 @@
      * extrapolation takes place and `resampleTime` is too far in the future. If `futureSample` is
      * not null, interpolation will occur. If `futureSample` is null and there is enough historical
      * data, LegacyResampler will extrapolate. Otherwise, no resampling takes place and
-     * `motionEvent` is unmodified.
+     * `motionEvent` is unmodified. Furthermore, motionEvent is not resampled if resampleTime equals
+     * the last sample eventTime of motionEvent.
      */
     void resampleMotionEvent(std::chrono::nanoseconds frameTime, MotionEvent& motionEvent,
                              const InputMessage* futureSample) override;
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 328fa68..1adff7b 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -249,6 +249,11 @@
                                           const InputMessage* futureSample) {
     const nanoseconds resampleTime = frameTime - RESAMPLE_LATENCY;
 
+    if (resampleTime.count() == motionEvent.getEventTime()) {
+        LOG_IF(INFO, debugResampling()) << "Not resampled. Resample time equals motion event time.";
+        return;
+    }
+
     updateLatestSamples(motionEvent);
 
     const std::optional<Sample> sample = (futureSample != nullptr)
diff --git a/libs/input/tests/InputConsumerResampling_test.cpp b/libs/input/tests/InputConsumerResampling_test.cpp
index 61a0a98..b139e87 100644
--- a/libs/input/tests/InputConsumerResampling_test.cpp
+++ b/libs/input/tests/InputConsumerResampling_test.cpp
@@ -1,4 +1,3 @@
-
 /*
  * Copyright (C) 2024 The Android Open Source Project
  *
@@ -193,7 +192,7 @@
  * last two real events, which would put this time at: 20 ms + (20 ms - 10 ms) / 2 = 25 ms.
  */
 TEST_F(InputConsumerResamplingTest, EventIsResampled) {
-    // Initial ACTION_DOWN should be separate, because the first consume event will only return
+    // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
     // InputEvent with a single action.
     mClientTestChannel->enqueueMessage(nextPointerMessage(
             {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
@@ -234,7 +233,7 @@
  * have these hardcoded.
  */
 TEST_F(InputConsumerResamplingTest, EventIsResampledWithDifferentId) {
-    // Initial ACTION_DOWN should be separate, because the first consume event will only return
+    // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
     // InputEvent with a single action.
     mClientTestChannel->enqueueMessage(nextPointerMessage(
             {0ms, {Pointer{.id = 1, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
@@ -274,7 +273,7 @@
  * Stylus pointer coordinates are resampled.
  */
 TEST_F(InputConsumerResamplingTest, StylusEventIsResampled) {
-    // Initial ACTION_DOWN should be separate, because the first consume event will only return
+    // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
     // InputEvent with a single action.
     mClientTestChannel->enqueueMessage(nextPointerMessage(
             {0ms,
@@ -332,9 +331,8 @@
  * Mouse pointer coordinates are resampled.
  */
 TEST_F(InputConsumerResamplingTest, MouseEventIsResampled) {
-    // Initial ACTION_DOWN should be separate, because the first consume event will only return
+    // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
     // InputEvent with a single action.
-
     mClientTestChannel->enqueueMessage(nextPointerMessage(
             {0ms,
              {Pointer{.id = 0, .x = 10.0f, .y = 20.0f, .toolType = ToolType::MOUSE}},
@@ -391,7 +389,7 @@
  * Motion events with palm tool type are not resampled.
  */
 TEST_F(InputConsumerResamplingTest, PalmEventIsNotResampled) {
-    // Initial ACTION_DOWN should be separate, because the first consume event will only return
+    // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
     // InputEvent with a single action.
     mClientTestChannel->enqueueMessage(nextPointerMessage(
             {0ms,
@@ -431,4 +429,43 @@
     mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
 }
 
+/**
+ * Event should not be resampled when sample time is equal to event time.
+ */
+TEST_F(InputConsumerResamplingTest, SampleTimeEqualsEventTime) {
+    // Send the initial ACTION_DOWN separately, so that the first consumed event will only return an
+    // InputEvent with a single action.
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {0ms, {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}}, AMOTION_EVENT_ACTION_DOWN}));
+
+    mClientTestChannel->assertNoSentMessages();
+
+    invokeLooperCallback();
+    assertReceivedMotionEvent({InputEventEntry{0ms,
+                                               {Pointer{.id = 0, .x = 10.0f, .y = 20.0f}},
+                                               AMOTION_EVENT_ACTION_DOWN}});
+
+    // Two ACTION_MOVE events 10 ms apart that move in X direction and stay still in Y
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {10ms, {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+    mClientTestChannel->enqueueMessage(nextPointerMessage(
+            {20ms, {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}}, AMOTION_EVENT_ACTION_MOVE}));
+
+    invokeLooperCallback();
+    mConsumer->consumeBatchedInputEvents(nanoseconds{20ms + 5ms /*RESAMPLE_LATENCY*/}.count());
+
+    // MotionEvent should not resampled because the resample time falls exactly on the existing
+    // event time.
+    assertReceivedMotionEvent({InputEventEntry{10ms,
+                                               {Pointer{.id = 0, .x = 20.0f, .y = 30.0f}},
+                                               AMOTION_EVENT_ACTION_MOVE},
+                               InputEventEntry{20ms,
+                                               {Pointer{.id = 0, .x = 30.0f, .y = 30.0f}},
+                                               AMOTION_EVENT_ACTION_MOVE}});
+
+    mClientTestChannel->assertFinishMessage(/*seq=*/1, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/2, /*handled=*/true);
+    mClientTestChannel->assertFinishMessage(/*seq=*/3, /*handled=*/true);
+}
+
 } // namespace android