Revert "Make VelocityTracker 1D"

Revert "Conform to 1D VelocityTracker"

Revert submission 19762050-generic_vt

Reason for revert: b/245842062
Reverted Changes:
I181af7a03:Make VelocityTracker 1D
Ib0be0fc38:Conform to 1D VelocityTracker

Change-Id: Ife5675e4abdf154eb6466f687e52b6a427860d26
diff --git a/include/input/VelocityControl.h b/include/input/VelocityControl.h
index f4c7061..1acc2ae 100644
--- a/include/input/VelocityControl.h
+++ b/include/input/VelocityControl.h
@@ -98,7 +98,7 @@
     VelocityControlParameters mParameters;
 
     nsecs_t mLastMovementTime;
-    float mRawPositionX, mRawPositionY;
+    VelocityTracker::Position mRawPosition;
     VelocityTracker mVelocityTracker;
 };
 
diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h
index 6f2fcf4..886f1f7 100644
--- a/include/input/VelocityTracker.h
+++ b/include/input/VelocityTracker.h
@@ -20,8 +20,6 @@
 #include <input/Input.h>
 #include <utils/BitSet.h>
 #include <utils/Timers.h>
-#include <map>
-#include <set>
 
 namespace android {
 
@@ -48,14 +46,18 @@
         MAX = LEGACY,
     };
 
+    struct Position {
+        float x, y;
+    };
+
     struct Estimator {
         static const size_t MAX_DEGREE = 4;
 
         // Estimator time base.
         nsecs_t time;
 
-        // Polynomial coefficients describing motion.
-        float coeff[MAX_DEGREE + 1];
+        // Polynomial coefficients describing motion in X and Y.
+        float xCoeff[MAX_DEGREE + 1], yCoeff[MAX_DEGREE + 1];
 
         // Polynomial degree (number of coefficients), or zero if no information is
         // available.
@@ -69,40 +71,14 @@
             degree = 0;
             confidence = 0;
             for (size_t i = 0; i <= MAX_DEGREE; i++) {
-                coeff[i] = 0;
+                xCoeff[i] = 0;
+                yCoeff[i] = 0;
             }
         }
     };
 
-    /*
-     * Contains all available velocity data from a VelocityTracker.
-     */
-    struct ComputedVelocity {
-        inline std::optional<float> getVelocity(int32_t axis, uint32_t id) const {
-            const auto& axisVelocities = mVelocities.find(axis);
-            if (axisVelocities == mVelocities.end()) {
-                return {};
-            }
-
-            const auto& axisIdVelocity = axisVelocities->second.find(id);
-            if (axisIdVelocity == axisVelocities->second.end()) {
-                return {};
-            }
-
-            return axisIdVelocity->second;
-        }
-
-        inline void addVelocity(int32_t axis, uint32_t id, float velocity) {
-            mVelocities[axis][id] = velocity;
-        }
-
-    private:
-        std::map<int32_t /*axis*/, std::map<int32_t /*pointerId*/, float /*velocity*/>> mVelocities;
-    };
-
-    // Creates a velocity tracker using the specified strategy for each supported axis.
+    // Creates a velocity tracker using the specified strategy.
     // If strategy is not provided, uses the default strategy for the platform.
-    // TODO(b/32830165): support axis-specific strategies.
     VelocityTracker(const Strategy strategy = Strategy::DEFAULT);
 
     ~VelocityTracker();
@@ -116,57 +92,45 @@
     void clearPointers(BitSet32 idBits);
 
     // Adds movement information for a set of pointers.
-    // The idBits bitfield specifies the pointer ids of the pointers whose data points
+    // The idBits bitfield specifies the pointer ids of the pointers whose positions
     // are included in the movement.
-    // The positions map contains a mapping of an axis to positions array.
-    // The positions arrays contain information for each pointer in order by increasing id.
-    // Each array's size should be equal to the number of one bits in idBits.
-    void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::map<int32_t, std::vector<float>>& positions);
+    // The positions array contains position information for each pointer in order by
+    // increasing id.  Its size should be equal to the number of one bits in idBits.
+    void addMovement(nsecs_t eventTime, BitSet32 idBits, const std::vector<Position>& positions);
 
     // Adds movement information for all pointers in a MotionEvent, including historical samples.
     void addMovement(const MotionEvent* event);
 
-    // Returns the velocity of the specified pointer id and axis in position units per second.
-    // Returns empty optional if there is insufficient movement information for the pointer, or if
-    // the given axis is not supported for velocity tracking.
-    std::optional<float> getVelocity(int32_t axis, uint32_t id) const;
+    // Gets the velocity of the specified pointer id in position units per second.
+    // Returns false and sets the velocity components to zero if there is
+    // insufficient movement information for the pointer.
+    bool getVelocity(uint32_t id, float* outVx, float* outVy) const;
 
-    // Populates a ComputedVelocity instance with all available velocity data, using the given units
-    // (reference: units == 1 means "per millisecond"), and clamping each velocity between
-    // [-maxVelocity, maxVelocity], inclusive.
-    void populateComputedVelocity(ComputedVelocity& computedVelocity, int32_t units,
-                                  float maxVelocity);
-
-    // Gets an estimator for the recent movements of the specified pointer id for the given axis.
+    // Gets an estimator for the recent movements of the specified pointer id.
     // Returns false and clears the estimator if there is no information available
     // about the pointer.
-    bool getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const;
+    bool getEstimator(uint32_t id, Estimator* outEstimator) const;
 
     // Gets the active pointer id, or -1 if none.
     inline int32_t getActivePointerId() const { return mActivePointerId; }
 
+    // Gets a bitset containing all pointer ids from the most recent movement.
+    inline BitSet32 getCurrentPointerIdBits() const { return mCurrentPointerIdBits; }
+
 private:
     // The default velocity tracker strategy.
     // Although other strategies are available for testing and comparison purposes,
     // this is the strategy that applications will actually use.  Be very careful
     // when adjusting the default strategy because it can dramatically affect
     // (often in a bad way) the user experience.
-    // TODO(b/32830165): define default strategy per axis.
     static const Strategy DEFAULT_STRATEGY = Strategy::LSQ2;
 
-    // Set of all axes supported for velocity tracking.
-    static const std::set<int32_t> SUPPORTED_AXES;
-
-    // Axes specifying location on a 2D plane (i.e. X and Y).
-    static const std::set<int32_t> PLANAR_AXES;
-
     nsecs_t mLastEventTime;
     BitSet32 mCurrentPointerIdBits;
     int32_t mActivePointerId;
-    std::map<int32_t /*axis*/, std::unique_ptr<VelocityTrackerStrategy>> mStrategies;
+    std::unique_ptr<VelocityTrackerStrategy> mStrategy;
 
-    void configureStrategy(int32_t axis, const Strategy strategy);
+    bool configureStrategy(const Strategy strategy);
 
     static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy);
 };
@@ -185,7 +149,7 @@
     virtual void clear() = 0;
     virtual void clearPointers(BitSet32 idBits) = 0;
     virtual void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                             const std::vector<float>& positions) = 0;
+                             const std::vector<VelocityTracker::Position>& positions) = 0;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const = 0;
 };
 
@@ -217,7 +181,7 @@
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<float>& positions) override;
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -232,9 +196,11 @@
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
-        float positions[MAX_POINTERS];
+        VelocityTracker::Position positions[MAX_POINTERS];
 
-        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
+        inline const VelocityTracker::Position& getPosition(uint32_t id) const {
+            return positions[idBits.getIndexOfBit(id)];
+        }
     };
 
     float chooseWeight(uint32_t index) const;
@@ -258,7 +224,7 @@
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<float>& positions) override;
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -267,15 +233,16 @@
         nsecs_t updateTime;
         uint32_t degree;
 
-        float pos, vel, accel;
+        float xpos, xvel, xaccel;
+        float ypos, yvel, yaccel;
     };
 
     const uint32_t mDegree;
     BitSet32 mPointerIdBits;
     State mPointerState[MAX_POINTER_ID + 1];
 
-    void initState(State& state, nsecs_t eventTime, float pos) const;
-    void updateState(State& state, nsecs_t eventTime, float pos) const;
+    void initState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
+    void updateState(State& state, nsecs_t eventTime, float xpos, float ypos) const;
     void populateEstimator(const State& state, VelocityTracker::Estimator* outEstimator) const;
 };
 
@@ -291,7 +258,7 @@
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<float>& positions) override;
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -307,9 +274,11 @@
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
-        float positions[MAX_POINTERS];
+        VelocityTracker::Position positions[MAX_POINTERS];
 
-        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
+        inline const VelocityTracker::Position& getPosition(uint32_t id) const {
+            return positions[idBits.getIndexOfBit(id)];
+        }
     };
 
     uint32_t mIndex;
@@ -324,7 +293,7 @@
     virtual void clear();
     virtual void clearPointers(BitSet32 idBits);
     void addMovement(nsecs_t eventTime, BitSet32 idBits,
-                     const std::vector<float>& positions) override;
+                     const std::vector<VelocityTracker::Position>& positions) override;
     virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const;
 
 private:
@@ -339,9 +308,11 @@
     struct Movement {
         nsecs_t eventTime;
         BitSet32 idBits;
-        float positions[MAX_POINTERS];
+        VelocityTracker::Position positions[MAX_POINTERS];
 
-        inline float getPosition(uint32_t id) const { return positions[idBits.getIndexOfBit(id)]; }
+        inline const VelocityTracker::Position& getPosition(uint32_t id) const {
+            return positions[idBits.getIndexOfBit(id)];
+        }
     };
 
     size_t mIndex;
diff --git a/libs/input/VelocityControl.cpp b/libs/input/VelocityControl.cpp
index e2bfb50..6e991e9 100644
--- a/libs/input/VelocityControl.cpp
+++ b/libs/input/VelocityControl.cpp
@@ -44,8 +44,8 @@
 
 void VelocityControl::reset() {
     mLastMovementTime = LLONG_MIN;
-    mRawPositionX = 0;
-    mRawPositionY = 0;
+    mRawPosition.x = 0;
+    mRawPosition.y = 0;
     mVelocityTracker.clear();
 }
 
@@ -61,20 +61,17 @@
 
         mLastMovementTime = eventTime;
         if (deltaX) {
-            mRawPositionX += *deltaX;
+            mRawPosition.x += *deltaX;
         }
         if (deltaY) {
-            mRawPositionY += *deltaY;
+            mRawPosition.y += *deltaY;
         }
-        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)),
-                                     {{AMOTION_EVENT_AXIS_X, {mRawPositionX}},
-                                      {AMOTION_EVENT_AXIS_Y, {mRawPositionY}}});
+        mVelocityTracker.addMovement(eventTime, BitSet32(BitSet32::valueForBit(0)), {mRawPosition});
 
-        std::optional<float> vx = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, 0);
-        std::optional<float> vy = mVelocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, 0);
+        float vx, vy;
         float scale = mParameters.scale;
-        if (vx && vy) {
-            float speed = hypotf(*vx, *vy) * scale;
+        if (mVelocityTracker.getVelocity(0, &vx, &vy)) {
+            float speed = hypotf(vx, vy) * scale;
             if (speed >= mParameters.highThreshold) {
                 // Apply full acceleration above the high speed threshold.
                 scale *= mParameters.acceleration;
@@ -88,9 +85,10 @@
 
             if (DEBUG_ACCELERATION) {
                 ALOGD("VelocityControl(%0.3f, %0.3f, %0.3f, %0.3f): "
-                      "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
-                      mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
-                      mParameters.acceleration, *vx, *vy, speed, scale / mParameters.scale);
+                        "vx=%0.3f, vy=%0.3f, speed=%0.3f, accel=%0.3f",
+                        mParameters.scale, mParameters.lowThreshold, mParameters.highThreshold,
+                        mParameters.acceleration,
+                        vx, vy, speed, scale / mParameters.scale);
             }
 
         } else {
diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp
index 4f91af1..76aaf61 100644
--- a/libs/input/VelocityTracker.cpp
+++ b/libs/input/VelocityTracker.cpp
@@ -125,39 +125,29 @@
 
 // --- VelocityTracker ---
 
-const std::set<int32_t> VelocityTracker::SUPPORTED_AXES = {AMOTION_EVENT_AXIS_X,
-                                                           AMOTION_EVENT_AXIS_Y};
-
-const std::set<int32_t> VelocityTracker::PLANAR_AXES = {AMOTION_EVENT_AXIS_X, AMOTION_EVENT_AXIS_Y};
-
 VelocityTracker::VelocityTracker(const Strategy strategy)
       : mLastEventTime(0), mCurrentPointerIdBits(0), mActivePointerId(-1) {
-    // Configure the strategy for each axis.
-    for (int32_t axis : SUPPORTED_AXES) {
-        configureStrategy(axis, strategy);
+    // Configure the strategy.
+    if (!configureStrategy(strategy)) {
+        ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
+        if (!configureStrategy(VelocityTracker::DEFAULT_STRATEGY)) {
+            LOG_ALWAYS_FATAL("Could not create the default velocity tracker strategy '%" PRId32
+                             "'!",
+                             strategy);
+        }
     }
 }
 
 VelocityTracker::~VelocityTracker() {
 }
 
-void VelocityTracker::configureStrategy(int32_t axis, const Strategy strategy) {
-    std::unique_ptr<VelocityTrackerStrategy> createdStrategy;
-
+bool VelocityTracker::configureStrategy(Strategy strategy) {
     if (strategy == VelocityTracker::Strategy::DEFAULT) {
-        createdStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
+        mStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
     } else {
-        createdStrategy = createStrategy(strategy);
+        mStrategy = createStrategy(strategy);
     }
-
-    if (createdStrategy == nullptr) {
-        ALOGE("Unrecognized velocity tracker strategy %" PRId32 ".", strategy);
-        createdStrategy = createStrategy(VelocityTracker::DEFAULT_STRATEGY);
-        LOG_ALWAYS_FATAL_IF(createdStrategy == nullptr,
-                            "Could not create the default velocity tracker strategy '%" PRId32 "'!",
-                            strategy);
-    }
-    mStrategies[axis] = std::move(createdStrategy);
+    return mStrategy != nullptr;
 }
 
 std::unique_ptr<VelocityTrackerStrategy> VelocityTracker::createStrategy(
@@ -211,9 +201,8 @@
 void VelocityTracker::clear() {
     mCurrentPointerIdBits.clear();
     mActivePointerId = -1;
-    for (int32_t axis : SUPPORTED_AXES) {
-        mStrategies[axis]->clear();
-    }
+
+    mStrategy->clear();
 }
 
 void VelocityTracker::clearPointers(BitSet32 idBits) {
@@ -224,13 +213,14 @@
         mActivePointerId = !remainingIdBits.isEmpty() ? remainingIdBits.firstMarkedBit() : -1;
     }
 
-    for (int32_t axis : SUPPORTED_AXES) {
-        mStrategies[axis]->clearPointers(idBits);
-    }
+    mStrategy->clearPointers(idBits);
 }
 
 void VelocityTracker::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                  const std::map<int32_t /*axis*/, std::vector<float>>& positions) {
+                                  const std::vector<VelocityTracker::Position>& positions) {
+    LOG_ALWAYS_FATAL_IF(idBits.count() != positions.size(),
+                        "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
+                        idBits.count(), positions.size());
     while (idBits.count() > MAX_POINTERS) {
         idBits.clearLastMarkedBit();
     }
@@ -242,9 +232,7 @@
 
         // We have not received any movements for too long.  Assume that all pointers
         // have stopped.
-        for (const auto& [_, strategy] : mStrategies) {
-            strategy->clear();
-        }
+        mStrategy->clear();
     }
     mLastEventTime = eventTime;
 
@@ -253,37 +241,29 @@
         mActivePointerId = idBits.isEmpty() ? -1 : idBits.firstMarkedBit();
     }
 
-    for (const auto& [axis, positionValues] : positions) {
-        LOG_ALWAYS_FATAL_IF(idBits.count() != positionValues.size(),
-                            "Mismatching number of pointers, idBits=%" PRIu32 ", positions=%zu",
-                            idBits.count(), positionValues.size());
-        mStrategies[axis]->addMovement(eventTime, idBits, positionValues);
-    }
+    mStrategy->addMovement(eventTime, idBits, positions);
 
     if (DEBUG_VELOCITY) {
         ALOGD("VelocityTracker: addMovement eventTime=%" PRId64
               ", idBits=0x%08x, activePointerId=%d",
               eventTime, idBits.value, mActivePointerId);
-        for (const auto& positionsEntry : positions) {
-            for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
-                uint32_t id = iterBits.firstMarkedBit();
-                uint32_t index = idBits.getIndexOfBit(id);
-                iterBits.clearBit(id);
-                Estimator estimator;
-                getEstimator(positionsEntry.first, id, &estimator);
-                ALOGD("  %d: axis=%d, position=%0.3f, "
-                      "estimator (degree=%d, coeff=%s, confidence=%f)",
-                      id, positionsEntry.first, positionsEntry.second[index], int(estimator.degree),
-                      vectorToString(estimator.coeff, estimator.degree + 1).c_str(),
-                      estimator.confidence);
-            }
+        for (BitSet32 iterBits(idBits); !iterBits.isEmpty();) {
+            uint32_t id = iterBits.firstMarkedBit();
+            uint32_t index = idBits.getIndexOfBit(id);
+            iterBits.clearBit(id);
+            Estimator estimator;
+            getEstimator(id, &estimator);
+            ALOGD("  %d: position (%0.3f, %0.3f), "
+                  "estimator (degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f)",
+                  id, positions[index].x, positions[index].y, int(estimator.degree),
+                  vectorToString(estimator.xCoeff, estimator.degree + 1).c_str(),
+                  vectorToString(estimator.yCoeff, estimator.degree + 1).c_str(),
+                  estimator.confidence);
         }
     }
 }
 
 void VelocityTracker::addMovement(const MotionEvent* event) {
-    // Stores data about which axes to process based on the incoming motion event.
-    std::set<int32_t> axesToProcess;
     int32_t actionMasked = event->getActionMasked();
 
     switch (actionMasked) {
@@ -291,9 +271,6 @@
     case AMOTION_EVENT_ACTION_HOVER_ENTER:
         // Clear all pointers on down before adding the new movement.
         clear();
-        for (int32_t axis : PLANAR_AXES) {
-            axesToProcess.insert(axis);
-        }
         break;
     case AMOTION_EVENT_ACTION_POINTER_DOWN: {
         // Start a new movement trace for a pointer that just went down.
@@ -302,16 +279,10 @@
         BitSet32 downIdBits;
         downIdBits.markBit(event->getPointerId(event->getActionIndex()));
         clearPointers(downIdBits);
-        for (int32_t axis : PLANAR_AXES) {
-            axesToProcess.insert(axis);
-        }
         break;
     }
     case AMOTION_EVENT_ACTION_MOVE:
     case AMOTION_EVENT_ACTION_HOVER_MOVE:
-        for (int32_t axis : PLANAR_AXES) {
-            axesToProcess.insert(axis);
-        }
         break;
     case AMOTION_EVENT_ACTION_POINTER_UP:
     case AMOTION_EVENT_ACTION_UP: {
@@ -322,9 +293,7 @@
                      toString(delaySinceLastEvent).c_str());
             // We have not received any movements for too long.  Assume that all pointers
             // have stopped.
-            for (int32_t axis : PLANAR_AXES) {
-                mStrategies[axis]->clear();
-            }
+            mStrategy->clear();
         }
         // These actions because they do not convey any new information about
         // pointer movement.  We also want to preserve the last known velocity of the pointers.
@@ -356,54 +325,37 @@
         pointerIndex[i] = idBits.getIndexOfBit(event->getPointerId(i));
     }
 
-    std::map<int32_t, std::vector<float>> positions;
-    for (int32_t axis : axesToProcess) {
-        positions[axis].resize(pointerCount);
-    }
+    std::vector<Position> positions;
+    positions.resize(pointerCount);
 
     size_t historySize = event->getHistorySize();
     for (size_t h = 0; h <= historySize; h++) {
         nsecs_t eventTime = event->getHistoricalEventTime(h);
-        for (int32_t axis : axesToProcess) {
-            for (size_t i = 0; i < pointerCount; i++) {
-                positions[axis][pointerIndex[i]] = event->getHistoricalAxisValue(axis, i, h);
-            }
+        for (size_t i = 0; i < pointerCount; i++) {
+            uint32_t index = pointerIndex[i];
+            positions[index].x = event->getHistoricalX(i, h);
+            positions[index].y = event->getHistoricalY(i, h);
         }
         addMovement(eventTime, idBits, positions);
     }
 }
 
-std::optional<float> VelocityTracker::getVelocity(int32_t axis, uint32_t id) const {
+bool VelocityTracker::getVelocity(uint32_t id, float* outVx, float* outVy) const {
     Estimator estimator;
-    bool validVelocity = getEstimator(axis, id, &estimator) && estimator.degree >= 1;
-    if (validVelocity) {
-        return estimator.coeff[1];
+    if (getEstimator(id, &estimator) && estimator.degree >= 1) {
+        *outVx = estimator.xCoeff[1];
+        *outVy = estimator.yCoeff[1];
+        return true;
     }
-    return {};
+    *outVx = 0;
+    *outVy = 0;
+    return false;
 }
 
-void VelocityTracker::populateComputedVelocity(ComputedVelocity& computedVelocity, int32_t units,
-                                               float maxVelocity) {
-    for (int32_t axis : SUPPORTED_AXES) {
-        BitSet32 copyIdBits = BitSet32(mCurrentPointerIdBits);
-        while (!copyIdBits.isEmpty()) {
-            uint32_t id = copyIdBits.clearFirstMarkedBit();
-            std::optional<float> velocity = getVelocity(axis, id);
-            if (velocity) {
-                float adjustedVelocity =
-                        std::clamp(*velocity * units / 1000, -maxVelocity, maxVelocity);
-                computedVelocity.addVelocity(axis, id, adjustedVelocity);
-            }
-        }
-    }
+bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const {
+    return mStrategy->getEstimator(id, outEstimator);
 }
 
-bool VelocityTracker::getEstimator(int32_t axis, uint32_t id, Estimator* outEstimator) const {
-    if (SUPPORTED_AXES.find(axis) == SUPPORTED_AXES.end()) {
-        return false;
-    }
-    return mStrategies.at(axis)->getEstimator(id, outEstimator);
-}
 
 // --- LeastSquaresVelocityTrackerStrategy ---
 
@@ -426,8 +378,9 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LeastSquaresVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                                      const std::vector<float>& positions) {
+void LeastSquaresVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -674,7 +627,8 @@
     outEstimator->clear();
 
     // Iterate over movement samples in reverse time order and collect samples.
-    std::vector<float> positions;
+    std::vector<float> x;
+    std::vector<float> y;
     std::vector<float> w;
     std::vector<float> time;
 
@@ -691,13 +645,15 @@
             break;
         }
 
-        positions.push_back(movement.getPosition(id));
+        const VelocityTracker::Position& position = movement.getPosition(id);
+        x.push_back(position.x);
+        y.push_back(position.y);
         w.push_back(chooseWeight(index));
         time.push_back(-age * 0.000000001f);
         index = (index == 0 ? HISTORY_SIZE : index) - 1;
-    } while (positions.size() < HISTORY_SIZE);
+    } while (x.size() < HISTORY_SIZE);
 
-    const size_t m = positions.size();
+    const size_t m = x.size();
     if (m == 0) {
         return false; // no data
     }
@@ -710,36 +666,39 @@
 
     if (degree == 2 && mWeighting == WEIGHTING_NONE) {
         // Optimize unweighted, quadratic polynomial fit
-        std::optional<std::array<float, 3>> coeff =
-                solveUnweightedLeastSquaresDeg2(time, positions);
-        if (coeff) {
+        std::optional<std::array<float, 3>> xCoeff = solveUnweightedLeastSquaresDeg2(time, x);
+        std::optional<std::array<float, 3>> yCoeff = solveUnweightedLeastSquaresDeg2(time, y);
+        if (xCoeff && yCoeff) {
             outEstimator->time = newestMovement.eventTime;
             outEstimator->degree = 2;
             outEstimator->confidence = 1;
             for (size_t i = 0; i <= outEstimator->degree; i++) {
-                outEstimator->coeff[i] = (*coeff)[i];
+                outEstimator->xCoeff[i] = (*xCoeff)[i];
+                outEstimator->yCoeff[i] = (*yCoeff)[i];
             }
             return true;
         }
     } else if (degree >= 1) {
         // General case for an Nth degree polynomial fit
-        float det;
+        float xdet, ydet;
         uint32_t n = degree + 1;
-        if (solveLeastSquares(time, positions, w, n, outEstimator->coeff, &det)) {
+        if (solveLeastSquares(time, x, w, n, outEstimator->xCoeff, &xdet) &&
+            solveLeastSquares(time, y, w, n, outEstimator->yCoeff, &ydet)) {
             outEstimator->time = newestMovement.eventTime;
             outEstimator->degree = degree;
-            outEstimator->confidence = det;
+            outEstimator->confidence = xdet * ydet;
 
-            ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, coeff=%s, confidence=%f",
-                     int(outEstimator->degree), vectorToString(outEstimator->coeff, n).c_str(),
-                     outEstimator->confidence);
+            ALOGD_IF(DEBUG_STRATEGY, "estimate: degree=%d, xCoeff=%s, yCoeff=%s, confidence=%f",
+                     int(outEstimator->degree), vectorToString(outEstimator->xCoeff, n).c_str(),
+                     vectorToString(outEstimator->yCoeff, n).c_str(), outEstimator->confidence);
 
             return true;
         }
     }
 
     // No velocity data available for this pointer, but we do have its current position.
-    outEstimator->coeff[0] = positions[0];
+    outEstimator->xCoeff[0] = x[0];
+    outEstimator->yCoeff[0] = y[0];
     outEstimator->time = newestMovement.eventTime;
     outEstimator->degree = 0;
     outEstimator->confidence = 1;
@@ -831,17 +790,18 @@
     mPointerIdBits.value &= ~idBits.value;
 }
 
-void IntegratingVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                                     const std::vector<float>& positions) {
+void IntegratingVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     uint32_t index = 0;
     for (BitSet32 iterIdBits(idBits); !iterIdBits.isEmpty();) {
         uint32_t id = iterIdBits.clearFirstMarkedBit();
         State& state = mPointerState[id];
-        const float position = positions[index++];
+        const VelocityTracker::Position& position = positions[index++];
         if (mPointerIdBits.hasBit(id)) {
-            updateState(state, eventTime, position);
+            updateState(state, eventTime, position.x, position.y);
         } else {
-            initState(state, eventTime, position);
+            initState(state, eventTime, position.x, position.y);
         }
     }
 
@@ -861,18 +821,21 @@
     return false;
 }
 
-void IntegratingVelocityTrackerStrategy::initState(State& state, nsecs_t eventTime,
-                                                   float pos) const {
+void IntegratingVelocityTrackerStrategy::initState(State& state,
+        nsecs_t eventTime, float xpos, float ypos) const {
     state.updateTime = eventTime;
     state.degree = 0;
 
-    state.pos = pos;
-    state.accel = 0;
-    state.vel = 0;
+    state.xpos = xpos;
+    state.xvel = 0;
+    state.xaccel = 0;
+    state.ypos = ypos;
+    state.yvel = 0;
+    state.yaccel = 0;
 }
 
-void IntegratingVelocityTrackerStrategy::updateState(State& state, nsecs_t eventTime,
-                                                     float pos) const {
+void IntegratingVelocityTrackerStrategy::updateState(State& state,
+        nsecs_t eventTime, float xpos, float ypos) const {
     const nsecs_t MIN_TIME_DELTA = 2 * NANOS_PER_MS;
     const float FILTER_TIME_CONSTANT = 0.010f; // 10 milliseconds
 
@@ -883,26 +846,34 @@
     float dt = (eventTime - state.updateTime) * 0.000000001f;
     state.updateTime = eventTime;
 
-    float vel = (pos - state.pos) / dt;
+    float xvel = (xpos - state.xpos) / dt;
+    float yvel = (ypos - state.ypos) / dt;
     if (state.degree == 0) {
-        state.vel = vel;
+        state.xvel = xvel;
+        state.yvel = yvel;
         state.degree = 1;
     } else {
         float alpha = dt / (FILTER_TIME_CONSTANT + dt);
         if (mDegree == 1) {
-            state.vel += (vel - state.vel) * alpha;
+            state.xvel += (xvel - state.xvel) * alpha;
+            state.yvel += (yvel - state.yvel) * alpha;
         } else {
-            float accel = (vel - state.vel) / dt;
+            float xaccel = (xvel - state.xvel) / dt;
+            float yaccel = (yvel - state.yvel) / dt;
             if (state.degree == 1) {
-                state.accel = accel;
+                state.xaccel = xaccel;
+                state.yaccel = yaccel;
                 state.degree = 2;
             } else {
-                state.accel += (accel - state.accel) * alpha;
+                state.xaccel += (xaccel - state.xaccel) * alpha;
+                state.yaccel += (yaccel - state.yaccel) * alpha;
             }
-            state.vel += (state.accel * dt) * alpha;
+            state.xvel += (state.xaccel * dt) * alpha;
+            state.yvel += (state.yaccel * dt) * alpha;
         }
     }
-    state.pos = pos;
+    state.xpos = xpos;
+    state.ypos = ypos;
 }
 
 void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state,
@@ -910,9 +881,12 @@
     outEstimator->time = state.updateTime;
     outEstimator->confidence = 1.0f;
     outEstimator->degree = state.degree;
-    outEstimator->coeff[0] = state.pos;
-    outEstimator->coeff[1] = state.vel;
-    outEstimator->coeff[2] = state.accel / 2;
+    outEstimator->xCoeff[0] = state.xpos;
+    outEstimator->xCoeff[1] = state.xvel;
+    outEstimator->xCoeff[2] = state.xaccel / 2;
+    outEstimator->yCoeff[0] = state.ypos;
+    outEstimator->yCoeff[1] = state.yvel;
+    outEstimator->yCoeff[2] = state.yaccel / 2;
 }
 
 
@@ -935,8 +909,9 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void LegacyVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                                const std::vector<float>& positions) {
+void LegacyVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     if (++mIndex == HISTORY_SIZE) {
         mIndex = 0;
     }
@@ -984,11 +959,12 @@
     // overestimate the velocity at that time point.  Most samples might be measured
     // 16ms apart but some consecutive samples could be only 0.5sm apart because
     // the hardware or driver reports them irregularly or in bursts.
-    float accumV = 0;
+    float accumVx = 0;
+    float accumVy = 0;
     uint32_t index = oldestIndex;
     uint32_t samplesUsed = 0;
     const Movement& oldestMovement = mMovements[oldestIndex];
-    float oldestPosition = oldestMovement.getPosition(id);
+    const VelocityTracker::Position& oldestPosition = oldestMovement.getPosition(id);
     nsecs_t lastDuration = 0;
 
     while (numTouches-- > 1) {
@@ -1002,22 +978,26 @@
         // the velocity.  Consequently, we impose a minimum duration constraint on the
         // samples that we include in the calculation.
         if (duration >= MIN_DURATION) {
-            float position = movement.getPosition(id);
+            const VelocityTracker::Position& position = movement.getPosition(id);
             float scale = 1000000000.0f / duration; // one over time delta in seconds
-            float v = (position - oldestPosition) * scale;
-            accumV = (accumV * lastDuration + v * duration) / (duration + lastDuration);
+            float vx = (position.x - oldestPosition.x) * scale;
+            float vy = (position.y - oldestPosition.y) * scale;
+            accumVx = (accumVx * lastDuration + vx * duration) / (duration + lastDuration);
+            accumVy = (accumVy * lastDuration + vy * duration) / (duration + lastDuration);
             lastDuration = duration;
             samplesUsed += 1;
         }
     }
 
     // Report velocity.
-    float newestPosition = newestMovement.getPosition(id);
+    const VelocityTracker::Position& newestPosition = newestMovement.getPosition(id);
     outEstimator->time = newestMovement.eventTime;
     outEstimator->confidence = 1;
-    outEstimator->coeff[0] = newestPosition;
+    outEstimator->xCoeff[0] = newestPosition.x;
+    outEstimator->yCoeff[0] = newestPosition.y;
     if (samplesUsed) {
-        outEstimator->coeff[1] = accumV;
+        outEstimator->xCoeff[1] = accumVx;
+        outEstimator->yCoeff[1] = accumVy;
         outEstimator->degree = 1;
     } else {
         outEstimator->degree = 0;
@@ -1044,8 +1024,9 @@
     mMovements[mIndex].idBits = remainingIdBits;
 }
 
-void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits,
-                                                 const std::vector<float>& positions) {
+void ImpulseVelocityTrackerStrategy::addMovement(
+        nsecs_t eventTime, BitSet32 idBits,
+        const std::vector<VelocityTracker::Position>& positions) {
     if (mMovements[mIndex].eventTime != eventTime) {
         // When ACTION_POINTER_DOWN happens, we will first receive ACTION_MOVE with the coordinates
         // of the existing pointers, and then ACTION_POINTER_DOWN with the coordinates that include
@@ -1182,7 +1163,8 @@
     outEstimator->clear();
 
     // Iterate over movement samples in reverse time order and collect samples.
-    float positions[HISTORY_SIZE];
+    float x[HISTORY_SIZE];
+    float y[HISTORY_SIZE];
     nsecs_t time[HISTORY_SIZE];
     size_t m = 0; // number of points that will be used for fitting
     size_t index = mIndex;
@@ -1198,7 +1180,9 @@
             break;
         }
 
-        positions[m] = movement.getPosition(id);
+        const VelocityTracker::Position& position = movement.getPosition(id);
+        x[m] = position.x;
+        y[m] = position.y;
         time[m] = movement.eventTime;
         index = (index == 0 ? HISTORY_SIZE : index) - 1;
     } while (++m < HISTORY_SIZE);
@@ -1206,30 +1190,33 @@
     if (m == 0) {
         return false; // no data
     }
-    outEstimator->coeff[0] = 0;
-    outEstimator->coeff[1] = calculateImpulseVelocity(time, positions, m);
-    outEstimator->coeff[2] = 0;
-
+    outEstimator->xCoeff[0] = 0;
+    outEstimator->yCoeff[0] = 0;
+    outEstimator->xCoeff[1] = calculateImpulseVelocity(time, x, m);
+    outEstimator->yCoeff[1] = calculateImpulseVelocity(time, y, m);
+    outEstimator->xCoeff[2] = 0;
+    outEstimator->yCoeff[2] = 0;
     outEstimator->time = newestMovement.eventTime;
     outEstimator->degree = 2; // similar results to 2nd degree fit
     outEstimator->confidence = 1;
 
-    ALOGD_IF(DEBUG_STRATEGY, "velocity: %.1f", outEstimator->coeff[1]);
+    ALOGD_IF(DEBUG_STRATEGY, "velocity: (%.1f, %.1f)", outEstimator->xCoeff[1],
+             outEstimator->yCoeff[1]);
 
     if (DEBUG_IMPULSE) {
         // TODO(b/134179997): delete this block once the switch to 'impulse' is complete.
-        // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons.
-        // X axis chosen arbitrarily for velocity comparisons.
+        // Calculate the lsq2 velocity for the same inputs to allow runtime comparisons
         VelocityTracker lsq2(VelocityTracker::Strategy::LSQ2);
         BitSet32 idBits;
         const uint32_t pointerId = 0;
         idBits.markBit(pointerId);
         for (ssize_t i = m - 1; i >= 0; i--) {
-            lsq2.addMovement(time[i], idBits, {{AMOTION_EVENT_AXIS_X, {positions[i]}}});
+            lsq2.addMovement(time[i], idBits, {{x[i], y[i]}});
         }
-        std::optional<float> v = lsq2.getVelocity(AMOTION_EVENT_AXIS_X, pointerId);
-        if (v) {
-            ALOGD("lsq2 velocity: %.1f", *v);
+        float outVx = 0, outVy = 0;
+        const bool computed = lsq2.getVelocity(pointerId, &outVx, &outVy);
+        if (computed) {
+            ALOGD("lsq2 velocity: (%.1f, %.1f)", outVx, outVy);
         } else {
             ALOGD("lsq2 velocity: could not compute velocity");
         }
diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp
index 0a37318..4a445de 100644
--- a/libs/input/tests/VelocityTracker_test.cpp
+++ b/libs/input/tests/VelocityTracker_test.cpp
@@ -16,10 +16,9 @@
 
 #define LOG_TAG "VelocityTracker_test"
 
-#include <math.h>
 #include <array>
 #include <chrono>
-#include <limits>
+#include <math.h>
 
 #include <android-base/stringprintf.h>
 #include <attestation/HmacKeyManager.h>
@@ -199,13 +198,25 @@
                                     const std::vector<MotionEventEntry>& motions, int32_t axis,
                                     float targetVelocity, uint32_t pointerId = DEFAULT_POINTER_ID) {
     VelocityTracker vt(strategy);
+    float Vx, Vy;
 
     std::vector<MotionEvent> events = createMotionEventStream(motions);
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
 
-    checkVelocity(vt.getVelocity(axis, pointerId).value_or(0), targetVelocity);
+    vt.getVelocity(pointerId, &Vx, &Vy);
+
+    switch (axis) {
+    case AMOTION_EVENT_AXIS_X:
+        checkVelocity(Vx, targetVelocity);
+        break;
+    case AMOTION_EVENT_AXIS_Y:
+        checkVelocity(Vy, targetVelocity);
+        break;
+    default:
+        FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y";
+    }
 }
 
 static void computeAndCheckQuadraticEstimate(const std::vector<MotionEventEntry>& motions,
@@ -215,99 +226,17 @@
     for (MotionEvent event : events) {
         vt.addMovement(&event);
     }
-    VelocityTracker::Estimator estimatorX;
-    VelocityTracker::Estimator estimatorY;
-    EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_X, 0, &estimatorX));
-    EXPECT_TRUE(vt.getEstimator(AMOTION_EVENT_AXIS_Y, 0, &estimatorY));
+    VelocityTracker::Estimator estimator;
+    EXPECT_TRUE(vt.getEstimator(0, &estimator));
     for (size_t i = 0; i< coefficients.size(); i++) {
-        checkCoefficient(estimatorX.coeff[i], coefficients[i]);
-        checkCoefficient(estimatorY.coeff[i], coefficients[i]);
+        checkCoefficient(estimator.xCoeff[i], coefficients[i]);
+        checkCoefficient(estimator.yCoeff[i], coefficients[i]);
     }
 }
 
 /*
  * ================== VelocityTracker tests generated manually =====================================
  */
-TEST_F(VelocityTrackerTest, TestComputedVelocity) {
-    VelocityTracker::ComputedVelocity computedVelocity;
-
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 0 /*id*/, 200 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/, 400 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/, 650 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID, 750 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 0 /*id*/, 1000 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/, 2000 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/, 3000 /*velocity*/);
-    computedVelocity.addVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID, 4000 /*velocity*/);
-
-    // Check the axes/indices with velocity.
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 0U /*id*/)), 200);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 26U /*id*/)), 400);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, 27U /*id*/)), 650);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID)), 750);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 0U /*id*/)), 1000);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 26U /*id*/)), 2000);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, 27U /*id*/)), 3000);
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, MAX_POINTER_ID)), 4000);
-    for (uint32_t id = 0; id < 32; id++) {
-        // Since no data was added for AXIS_SCROLL, expect empty value for the axis for any id.
-        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, id))
-                << "Empty scroll data expected at id=" << id;
-        if (id == 0 || id == 26U || id == 27U || id == MAX_POINTER_ID) {
-            // Already checked above; continue.
-            continue;
-        }
-        // No data was added to X/Y for this id, expect empty value.
-        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, id))
-                << "Empty X data expected at id=" << id;
-        EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, id))
-                << "Empty Y data expected at id=" << id;
-    }
-    // Out-of-bounds ids should given empty values.
-    EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, -1));
-    EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, MAX_POINTER_ID + 1));
-}
-
-TEST_F(VelocityTrackerTest, TestPopulateComputedVelocity) {
-    std::vector<MotionEventEntry> motions = {
-            {235089067457000ns, {{528.00, 0}}}, {235089084684000ns, {{527.00, 0}}},
-            {235089093349000ns, {{527.00, 0}}}, {235089095677625ns, {{527.00, 0}}},
-            {235089101859000ns, {{527.00, 0}}}, {235089110378000ns, {{528.00, 0}}},
-            {235089112497111ns, {{528.25, 0}}}, {235089118760000ns, {{531.00, 0}}},
-            {235089126686000ns, {{535.00, 0}}}, {235089129316820ns, {{536.33, 0}}},
-            {235089135199000ns, {{540.00, 0}}}, {235089144297000ns, {{546.00, 0}}},
-            {235089146136443ns, {{547.21, 0}}}, {235089152923000ns, {{553.00, 0}}},
-            {235089160784000ns, {{559.00, 0}}}, {235089162955851ns, {{560.66, 0}}},
-            {235089162955851ns, {{560.66, 0}}}, // ACTION_UP
-    };
-    VelocityTracker vt(VelocityTracker::Strategy::IMPULSE);
-    std::vector<MotionEvent> events = createMotionEventStream(motions);
-    for (const MotionEvent& event : events) {
-        vt.addMovement(&event);
-    }
-
-    float maxFloat = std::numeric_limits<float>::max();
-    VelocityTracker::ComputedVelocity computedVelocity;
-    vt.populateComputedVelocity(computedVelocity, 1000 /* units */, maxFloat);
-    checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
-                  764.345703);
-
-    // Expect X velocity to be scaled with respective to provided units.
-    vt.populateComputedVelocity(computedVelocity, 1000000 /* units */, maxFloat);
-    checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)),
-                  764345.703);
-
-    // Expect X velocity to be clamped by provided max velocity.
-    vt.populateComputedVelocity(computedVelocity, 1000000 /* units */, 1000);
-    checkVelocity(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_X, DEFAULT_POINTER_ID)), 1000);
-
-    // All 0 data for Y; expect 0 velocity.
-    EXPECT_EQ(*(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_Y, DEFAULT_POINTER_ID)), 0);
-
-    // No data for scroll-axis; expect empty velocity.
-    EXPECT_FALSE(computedVelocity.getVelocity(AMOTION_EVENT_AXIS_SCROLL, DEFAULT_POINTER_ID));
-}
-
 TEST_F(VelocityTrackerTest, ThreePointsPositiveVelocityTest) {
     // Same coordinate is reported 2 times in a row
     // It is difficult to determine the correct answer here, but at least the direction
diff --git a/services/inputflinger/reader/mapper/TouchInputMapper.cpp b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
index 8c241f2..539e24a 100644
--- a/services/inputflinger/reader/mapper/TouchInputMapper.cpp
+++ b/services/inputflinger/reader/mapper/TouchInputMapper.cpp
@@ -2712,18 +2712,17 @@
 
     // Update the velocity tracker.
     {
-        std::vector<float> positionsX;
-        std::vector<float> positionsY;
+        std::vector<VelocityTracker::Position> positions;
         for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
             uint32_t id = idBits.clearFirstMarkedBit();
             const RawPointerData::Pointer& pointer =
                     mCurrentRawState.rawPointerData.pointerForId(id);
-            positionsX.push_back(pointer.x * mPointerXMovementScale);
-            positionsY.push_back(pointer.y * mPointerYMovementScale);
+            float x = pointer.x * mPointerXMovementScale;
+            float y = pointer.y * mPointerYMovementScale;
+            positions.push_back({x, y});
         }
         mPointerGesture.velocityTracker.addMovement(when, mCurrentCookedState.fingerIdBits,
-                                                    {{AMOTION_EVENT_AXIS_X, positionsX},
-                                                     {AMOTION_EVENT_AXIS_Y, positionsY}});
+                                                    positions);
     }
 
     // If the gesture ever enters a mode other than TAP, HOVER or TAP_DRAG, without first returning
@@ -2830,12 +2829,9 @@
             float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed;
             for (BitSet32 idBits(mCurrentCookedState.fingerIdBits); !idBits.isEmpty();) {
                 uint32_t id = idBits.clearFirstMarkedBit();
-                std::optional<float> vx =
-                        mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_X, id);
-                std::optional<float> vy =
-                        mPointerGesture.velocityTracker.getVelocity(AMOTION_EVENT_AXIS_Y, id);
-                if (vx && vy) {
-                    float speed = hypotf(*vx, *vy);
+                float vx, vy;
+                if (mPointerGesture.velocityTracker.getVelocity(id, &vx, &vy)) {
+                    float speed = hypotf(vx, vy);
                     if (speed > bestSpeed) {
                         bestId = id;
                         bestSpeed = speed;