Add multiple pointer support to LegacyResampler with tests

Added multiple pointer support to LegacyResampler and included the
corresponding unit tests to ensure correctness.

Bug: 297226446
Flag: EXEMPT refactor
Test: TEST=libinput_tests; m $TEST && $ANDROID_HOST_OUT/nativetest64/$TEST/$TEST --gtest_filter="ResamplerTest*"
Change-Id: Ib639942c31311dcdcf6d72ed33a4d4476b76fe7f
diff --git a/libs/input/Resampler.cpp b/libs/input/Resampler.cpp
index 342f7f5..a1e6e06 100644
--- a/libs/input/Resampler.cpp
+++ b/libs/input/Resampler.cpp
@@ -21,6 +21,7 @@
 
 #include <android-base/logging.h>
 #include <android-base/properties.h>
+#include <ftl/enum.h>
 
 #include <input/Resampler.h>
 #include <utils/Timers.h>
@@ -56,6 +57,11 @@
 
 constexpr std::chrono::milliseconds RESAMPLE_MAX_PREDICTION{8};
 
+bool canResampleTool(ToolType toolType) {
+    return toolType == ToolType::FINGER || toolType == ToolType::MOUSE ||
+            toolType == ToolType::STYLUS || toolType == ToolType::UNKNOWN;
+}
+
 inline float lerp(float a, float b, float alpha) {
     return a + alpha * (b - a);
 }
@@ -73,21 +79,71 @@
 
 void LegacyResampler::updateLatestSamples(const MotionEvent& motionEvent) {
     const size_t numSamples = motionEvent.getHistorySize() + 1;
-    for (size_t i = 0; i < numSamples; ++i) {
+    const size_t latestIndex = numSamples - 1;
+    const size_t secondToLatestIndex = (latestIndex > 0) ? (latestIndex - 1) : 0;
+    for (size_t sampleIndex = secondToLatestIndex; sampleIndex < numSamples; ++sampleIndex) {
+        std::vector<Pointer> pointers;
+        const size_t numPointers = motionEvent.getPointerCount();
+        for (size_t pointerIndex = 0; pointerIndex < numPointers; ++pointerIndex) {
+            // getSamplePointerCoords is the vector representation of a getHistorySize by
+            // getPointerCount matrix.
+            const PointerCoords& pointerCoords =
+                    motionEvent.getSamplePointerCoords()[sampleIndex * numPointers + pointerIndex];
+            pointers.push_back(
+                    Pointer{*motionEvent.getPointerProperties(pointerIndex), pointerCoords});
+        }
         mLatestSamples.pushBack(
-                Sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(i)),
-                       Pointer{*motionEvent.getPointerProperties(0),
-                               motionEvent.getSamplePointerCoords()[i]}});
+                Sample{static_cast<nanoseconds>(motionEvent.getHistoricalEventTime(sampleIndex)),
+                       pointers});
     }
 }
 
-bool LegacyResampler::canInterpolate(const InputMessage& futureSample) const {
+LegacyResampler::Sample LegacyResampler::messageToSample(const InputMessage& message) {
+    std::vector<Pointer> pointers;
+    for (uint32_t i = 0; i < message.body.motion.pointerCount; ++i) {
+        pointers.push_back(Pointer{message.body.motion.pointers[i].properties,
+                                   message.body.motion.pointers[i].coords});
+    }
+    return Sample{static_cast<nanoseconds>(message.body.motion.eventTime), pointers};
+}
+
+bool LegacyResampler::pointerPropertiesResampleable(const Sample& target, const Sample& auxiliary) {
+    if (target.pointers.size() > auxiliary.pointers.size()) {
+        LOG_IF(INFO, debugResampling())
+                << "Not resampled. Auxiliary sample has fewer pointers than target sample.";
+        return false;
+    }
+    for (size_t i = 0; i < target.pointers.size(); ++i) {
+        if (target.pointers[i].properties.id != auxiliary.pointers[i].properties.id) {
+            LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ID mismatch.";
+            return false;
+        }
+        if (target.pointers[i].properties.toolType != auxiliary.pointers[i].properties.toolType) {
+            LOG_IF(INFO, debugResampling()) << "Not resampled. Pointer ToolType mismatch.";
+            return false;
+        }
+        if (!canResampleTool(target.pointers[i].properties.toolType)) {
+            LOG_IF(INFO, debugResampling())
+                    << "Not resampled. Cannot resample "
+                    << ftl::enum_string(target.pointers[i].properties.toolType) << " ToolType.";
+            return false;
+        }
+    }
+    return true;
+}
+
+bool LegacyResampler::canInterpolate(const InputMessage& message) const {
     LOG_IF(FATAL, mLatestSamples.empty())
             << "Not resampled. mLatestSamples must not be empty to interpolate.";
 
     const Sample& pastSample = *(mLatestSamples.end() - 1);
-    const nanoseconds delta =
-            static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
+    const Sample& futureSample = messageToSample(message);
+
+    if (!pointerPropertiesResampleable(pastSample, futureSample)) {
+        return false;
+    }
+
+    const nanoseconds delta = futureSample.eventTime - pastSample.eventTime;
     if (delta < RESAMPLE_MIN_DELTA) {
         LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
         return false;
@@ -104,15 +160,20 @@
             << "Not resampled. mLatestSamples must not be empty to interpolate.";
 
     const Sample& pastSample = *(mLatestSamples.end() - 1);
+
     const nanoseconds delta =
             static_cast<nanoseconds>(futureSample.body.motion.eventTime) - pastSample.eventTime;
     const float alpha =
             std::chrono::duration<float, std::milli>(resampleTime - pastSample.eventTime) / delta;
-    const PointerCoords resampledCoords =
-            calculateResampledCoords(pastSample.pointer.coords,
-                                     futureSample.body.motion.pointers[0].coords, alpha);
 
-    return Sample{resampleTime, Pointer{pastSample.pointer.properties, resampledCoords}};
+    std::vector<Pointer> resampledPointers;
+    for (size_t i = 0; i < pastSample.pointers.size(); ++i) {
+        const PointerCoords& resampledCoords =
+                calculateResampledCoords(pastSample.pointers[i].coords,
+                                         futureSample.body.motion.pointers[i].coords, alpha);
+        resampledPointers.push_back(Pointer{pastSample.pointers[i].properties, resampledCoords});
+    }
+    return Sample{resampleTime, resampledPointers};
 }
 
 bool LegacyResampler::canExtrapolate() const {
@@ -124,6 +185,10 @@
     const Sample& pastSample = *(mLatestSamples.end() - 2);
     const Sample& presentSample = *(mLatestSamples.end() - 1);
 
+    if (!pointerPropertiesResampleable(presentSample, pastSample)) {
+        return false;
+    }
+
     const nanoseconds delta = presentSample.eventTime - pastSample.eventTime;
     if (delta < RESAMPLE_MIN_DELTA) {
         LOG_IF(INFO, debugResampling()) << "Not resampled. Delta is too small: " << delta << "ns.";
@@ -160,16 +225,21 @@
     const float alpha =
             std::chrono::duration<float, std::milli>(newResampleTime - pastSample.eventTime) /
             delta;
-    const PointerCoords resampledCoords =
-            calculateResampledCoords(pastSample.pointer.coords, presentSample.pointer.coords,
-                                     alpha);
 
-    return Sample{newResampleTime, Pointer{presentSample.pointer.properties, resampledCoords}};
+    std::vector<Pointer> resampledPointers;
+    for (size_t i = 0; i < presentSample.pointers.size(); ++i) {
+        const PointerCoords& resampledCoords =
+                calculateResampledCoords(pastSample.pointers[i].coords,
+                                         presentSample.pointers[i].coords, alpha);
+        resampledPointers.push_back(Pointer{presentSample.pointers[i].properties, resampledCoords});
+    }
+    return Sample{newResampleTime, resampledPointers};
 }
 
 inline void LegacyResampler::addSampleToMotionEvent(const Sample& sample,
                                                     MotionEvent& motionEvent) {
-    motionEvent.addSample(sample.eventTime.count(), &sample.pointer.coords, motionEvent.getId());
+    motionEvent.addSample(sample.eventTime.count(), sample.asPointerCoords().data(),
+                          motionEvent.getId());
 }
 
 void LegacyResampler::resampleMotionEvent(nanoseconds resampleTime, MotionEvent& motionEvent,