1 /* 2 * Copyright (C) 2012 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #pragma once 18 19 #include <android/os/IInputConstants.h> 20 #include <input/Input.h> 21 #include <input/RingBuffer.h> 22 #include <utils/BitSet.h> 23 #include <utils/Timers.h> 24 #include <map> 25 #include <set> 26 27 namespace android { 28 29 class VelocityTrackerStrategy; 30 31 /* 32 * Calculates the velocity of pointer movements over time. 33 */ 34 class VelocityTracker { 35 public: 36 static const size_t MAX_DEGREE = 4; 37 38 enum class Strategy : int32_t { 39 DEFAULT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_DEFAULT, 40 IMPULSE = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_IMPULSE, 41 LSQ1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ1, 42 LSQ2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ2, 43 LSQ3 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LSQ3, 44 WLSQ2_DELTA = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_DELTA, 45 WLSQ2_CENTRAL = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_CENTRAL, 46 WLSQ2_RECENT = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_WLSQ2_RECENT, 47 INT1 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT1, 48 INT2 = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_INT2, 49 LEGACY = android::os::IInputConstants::VELOCITY_TRACKER_STRATEGY_LEGACY, 50 MIN = IMPULSE, 51 MAX = LEGACY, 52 ftl_last = LEGACY, 53 }; 54 55 /* 56 * Contains all available velocity data from a VelocityTracker. 57 */ 58 struct ComputedVelocity { getVelocityComputedVelocity59 inline std::optional<float> getVelocity(int32_t axis, int32_t id) const { 60 const auto& axisVelocities = mVelocities.find(axis); 61 if (axisVelocities == mVelocities.end()) { 62 return {}; 63 } 64 65 const auto& axisIdVelocity = axisVelocities->second.find(id); 66 if (axisIdVelocity == axisVelocities->second.end()) { 67 return {}; 68 } 69 70 return axisIdVelocity->second; 71 } 72 addVelocityComputedVelocity73 inline void addVelocity(int32_t axis, int32_t id, float velocity) { 74 mVelocities[axis][id] = velocity; 75 } 76 77 private: 78 std::map<int32_t /*axis*/, std::map<int32_t /*pointerId*/, float /*velocity*/>> mVelocities; 79 }; 80 81 // Creates a velocity tracker using the specified strategy for each supported axis. 82 // If strategy is not provided, uses the default strategy for the platform. 83 // TODO(b/32830165): support axis-specific strategies. 84 VelocityTracker(const Strategy strategy = Strategy::DEFAULT); 85 86 /** Return true if the axis is supported for velocity tracking, false otherwise. */ 87 static bool isAxisSupported(int32_t axis); 88 89 // Resets the velocity tracker state. 90 void clear(); 91 92 // Resets the velocity tracker state for a specific pointer. 93 // Call this method when some pointers have changed and may be reusing 94 // an id that was assigned to a different pointer earlier. 95 void clearPointer(int32_t pointerId); 96 97 // Adds movement information for a pointer for a specific axis 98 void addMovement(nsecs_t eventTime, int32_t pointerId, int32_t axis, float position); 99 100 // Adds movement information for all pointers in a MotionEvent, including historical samples. 101 void addMovement(const MotionEvent& event); 102 103 // Returns the velocity of the specified pointer id and axis in position units per second. 104 // Returns empty optional if there is insufficient movement information for the pointer, or if 105 // the given axis is not supported for velocity tracking. 106 std::optional<float> getVelocity(int32_t axis, int32_t pointerId) const; 107 108 // Returns a ComputedVelocity instance with all available velocity data, using the given units 109 // (reference: units == 1 means "per millisecond"), and clamping each velocity between 110 // [-maxVelocity, maxVelocity], inclusive. 111 ComputedVelocity getComputedVelocity(int32_t units, float maxVelocity); 112 113 // Gets the active pointer id, or -1 if none. getActivePointerId()114 inline int32_t getActivePointerId() const { return mActivePointerId.value_or(-1); } 115 116 private: 117 nsecs_t mLastEventTime; 118 BitSet32 mCurrentPointerIdBits; 119 std::optional<int32_t> mActivePointerId; 120 121 // An override strategy passed in the constructor to be used for all axes. 122 // This strategy will apply to all axes, unless the default strategy is specified here. 123 // When default strategy is specified, then each axis will use a potentially different strategy 124 // based on a hardcoded mapping. 125 const Strategy mOverrideStrategy; 126 // Maps axes to their respective VelocityTrackerStrategy instances. 127 // Note that, only axes that have had MotionEvents (and not all supported axes) will be here. 128 std::map<int32_t /*axis*/, std::unique_ptr<VelocityTrackerStrategy>> mConfiguredStrategies; 129 130 void configureStrategy(int32_t axis); 131 132 // Generates a VelocityTrackerStrategy instance for the given Strategy type. 133 // The `deltaValues` parameter indicates whether or not the created strategy should treat motion 134 // values as deltas (and not as absolute values). This the parameter is applicable only for 135 // strategies that support differential axes. 136 static std::unique_ptr<VelocityTrackerStrategy> createStrategy(const Strategy strategy, 137 bool deltaValues); 138 }; 139 140 141 /* 142 * Implements a particular velocity tracker algorithm. 143 */ 144 class VelocityTrackerStrategy { 145 protected: VelocityTrackerStrategy()146 VelocityTrackerStrategy() { } 147 148 public: ~VelocityTrackerStrategy()149 virtual ~VelocityTrackerStrategy() { } 150 151 virtual void clearPointer(int32_t pointerId) = 0; 152 virtual void addMovement(nsecs_t eventTime, int32_t pointerId, float position) = 0; 153 virtual std::optional<float> getVelocity(int32_t pointerId) const = 0; 154 }; 155 156 /** 157 * A `VelocityTrackerStrategy` that accumulates added data points and processes the accumulated data 158 * points when getting velocity. 159 */ 160 class AccumulatingVelocityTrackerStrategy : public VelocityTrackerStrategy { 161 public: 162 AccumulatingVelocityTrackerStrategy(nsecs_t horizonNanos, bool maintainHorizonDuringAdd); 163 164 void addMovement(nsecs_t eventTime, int32_t pointerId, float position) override; 165 void clearPointer(int32_t pointerId) override; 166 167 protected: 168 struct Movement { 169 nsecs_t eventTime; 170 float position; 171 }; 172 173 // Number of samples to keep. 174 // If different strategies would like to maintain different history size, we can make this a 175 // protected const field. 176 static constexpr uint32_t HISTORY_SIZE = 20; 177 178 /** 179 * Duration, in nanoseconds, since the latest movement where a movement may be considered for 180 * velocity calculation. 181 */ 182 const nsecs_t mHorizonNanos; 183 /** 184 * If true, data points outside of horizon (see `mHorizonNanos`) will be cleared after each 185 * addition of a new movement. 186 */ 187 const bool mMaintainHorizonDuringAdd; 188 std::map<int32_t /*pointerId*/, RingBuffer<Movement>> mMovements; 189 }; 190 191 /* 192 * Velocity tracker algorithm based on least-squares linear regression. 193 */ 194 class LeastSquaresVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy { 195 public: 196 enum class Weighting { 197 // No weights applied. All data points are equally reliable. 198 NONE, 199 200 // Weight by time delta. Data points clustered together are weighted less. 201 DELTA, 202 203 // Weight such that points within a certain horizon are weighed more than those 204 // outside of that horizon. 205 CENTRAL, 206 207 // Weight such that points older than a certain amount are weighed less. 208 RECENT, 209 }; 210 211 // Degree must be no greater than VelocityTracker::MAX_DEGREE. 212 LeastSquaresVelocityTrackerStrategy(uint32_t degree, Weighting weighting = Weighting::NONE); 213 ~LeastSquaresVelocityTrackerStrategy() override; 214 215 std::optional<float> getVelocity(int32_t pointerId) const override; 216 217 private: 218 // Sample horizon. 219 // We don't use too much history by default since we want to react to quick 220 // changes in direction. 221 static const nsecs_t HORIZON = 100 * 1000000; // 100 ms 222 223 float chooseWeight(int32_t pointerId, uint32_t index) const; 224 /** 225 * An optimized least-squares solver for degree 2 and no weight (i.e. `Weighting.NONE`). 226 * The provided container of movements shall NOT be empty, and shall have the movements in 227 * chronological order. 228 */ 229 std::optional<float> solveUnweightedLeastSquaresDeg2( 230 const RingBuffer<Movement>& movements) const; 231 232 const uint32_t mDegree; 233 const Weighting mWeighting; 234 }; 235 236 /* 237 * Velocity tracker algorithm that uses an IIR filter. 238 */ 239 class IntegratingVelocityTrackerStrategy : public VelocityTrackerStrategy { 240 public: 241 // Degree must be 1 or 2. 242 IntegratingVelocityTrackerStrategy(uint32_t degree); 243 ~IntegratingVelocityTrackerStrategy() override; 244 245 void clearPointer(int32_t pointerId) override; 246 void addMovement(nsecs_t eventTime, int32_t pointerId, float positions) override; 247 std::optional<float> getVelocity(int32_t pointerId) const override; 248 249 private: 250 // Current state estimate for a particular pointer. 251 struct State { 252 nsecs_t updateTime; 253 uint32_t degree; 254 255 float pos, vel, accel; 256 }; 257 258 const uint32_t mDegree; 259 BitSet32 mPointerIdBits; 260 State mPointerState[MAX_POINTER_ID + 1]; 261 262 void initState(State& state, nsecs_t eventTime, float pos) const; 263 void updateState(State& state, nsecs_t eventTime, float pos) const; 264 }; 265 266 267 /* 268 * Velocity tracker strategy used prior to ICS. 269 */ 270 class LegacyVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy { 271 public: 272 LegacyVelocityTrackerStrategy(); 273 ~LegacyVelocityTrackerStrategy() override; 274 275 std::optional<float> getVelocity(int32_t pointerId) const override; 276 277 private: 278 // Oldest sample to consider when calculating the velocity. 279 static const nsecs_t HORIZON = 200 * 1000000; // 100 ms 280 281 // The minimum duration between samples when estimating velocity. 282 static const nsecs_t MIN_DURATION = 10 * 1000000; // 10 ms 283 }; 284 285 class ImpulseVelocityTrackerStrategy : public AccumulatingVelocityTrackerStrategy { 286 public: 287 ImpulseVelocityTrackerStrategy(bool deltaValues); 288 ~ImpulseVelocityTrackerStrategy() override; 289 290 std::optional<float> getVelocity(int32_t pointerId) const override; 291 292 private: 293 // Sample horizon. 294 // We don't use too much history by default since we want to react to quick 295 // changes in direction. 296 static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms 297 298 // Whether or not the input movement values for the strategy come in the form of delta values. 299 // If the input values are not deltas, the strategy needs to calculate deltas as part of its 300 // velocity calculation. 301 const bool mDeltaValues; 302 }; 303 304 } // namespace android 305