1 /* 2 * Copyright (C) 2020 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 package com.android.server.vibrator; 18 19 import android.annotation.Nullable; 20 import android.os.Handler; 21 import android.os.Looper; 22 import android.os.VibrationEffect; 23 import android.os.VibratorInfo; 24 import android.os.vibrator.PrebakedSegment; 25 import android.os.vibrator.PrimitiveSegment; 26 import android.os.vibrator.RampSegment; 27 import android.os.vibrator.StepSegment; 28 import android.os.vibrator.VibrationEffectSegment; 29 30 import com.android.server.vibrator.VibratorController.OnVibrationCompleteListener; 31 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.TreeMap; 38 39 /** 40 * Provides {@link VibratorController} with configurable vibrator hardware capabilities and 41 * fake interactions for tests. 42 */ 43 public final class FakeVibratorControllerProvider { 44 private static final int EFFECT_DURATION = 20; 45 46 private final Map<Long, PrebakedSegment> mEnabledAlwaysOnEffects = new HashMap<>(); 47 private final Map<Long, List<VibrationEffectSegment>> mEffectSegments = new TreeMap<>(); 48 private final Map<Long, List<Integer>> mBraking = new HashMap<>(); 49 private final List<Float> mAmplitudes = new ArrayList<>(); 50 private final List<Boolean> mExternalControlStates = new ArrayList<>(); 51 private final Handler mHandler; 52 private final FakeNativeWrapper mNativeWrapper; 53 54 private boolean mIsAvailable = true; 55 private boolean mIsInfoLoadSuccessful = true; 56 private long mCompletionCallbackDelay; 57 private long mOnLatency; 58 private long mOffLatency; 59 private int mOffCount; 60 61 private int mCapabilities; 62 private int[] mSupportedEffects; 63 private int[] mSupportedBraking; 64 private int[] mSupportedPrimitives; 65 private int mCompositionSizeMax; 66 private int mPwleSizeMax; 67 private float mMinFrequency = Float.NaN; 68 private float mResonantFrequency = Float.NaN; 69 private float mFrequencyResolution = Float.NaN; 70 private float mQFactor = Float.NaN; 71 private float[] mMaxAmplitudes; 72 recordEffectSegment(long vibrationId, VibrationEffectSegment segment)73 void recordEffectSegment(long vibrationId, VibrationEffectSegment segment) { 74 mEffectSegments.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(segment); 75 } 76 recordBraking(long vibrationId, int braking)77 void recordBraking(long vibrationId, int braking) { 78 mBraking.computeIfAbsent(vibrationId, k -> new ArrayList<>()).add(braking); 79 } 80 81 private final class FakeNativeWrapper extends VibratorController.NativeWrapper { 82 public int vibratorId; 83 public OnVibrationCompleteListener listener; 84 public boolean isInitialized; 85 86 @Override init(int vibratorId, OnVibrationCompleteListener listener)87 public void init(int vibratorId, OnVibrationCompleteListener listener) { 88 isInitialized = true; 89 this.vibratorId = vibratorId; 90 this.listener = listener; 91 } 92 93 @Override isAvailable()94 public boolean isAvailable() { 95 return mIsAvailable; 96 } 97 98 @Override on(long milliseconds, long vibrationId)99 public long on(long milliseconds, long vibrationId) { 100 recordEffectSegment(vibrationId, new StepSegment(VibrationEffect.DEFAULT_AMPLITUDE, 101 /* frequencyHz= */ 0, (int) milliseconds)); 102 applyLatency(mOnLatency); 103 scheduleListener(milliseconds, vibrationId); 104 return milliseconds; 105 } 106 107 @Override off()108 public void off() { 109 mOffCount++; 110 applyLatency(mOffLatency); 111 } 112 113 @Override setAmplitude(float amplitude)114 public void setAmplitude(float amplitude) { 115 mAmplitudes.add(amplitude); 116 applyLatency(mOnLatency); 117 } 118 119 @Override perform(long effect, long strength, long vibrationId)120 public long perform(long effect, long strength, long vibrationId) { 121 if (mSupportedEffects == null 122 || Arrays.binarySearch(mSupportedEffects, (int) effect) < 0) { 123 return 0; 124 } 125 recordEffectSegment(vibrationId, 126 new PrebakedSegment((int) effect, false, (int) strength)); 127 applyLatency(mOnLatency); 128 scheduleListener(EFFECT_DURATION, vibrationId); 129 return EFFECT_DURATION; 130 } 131 132 @Override compose(PrimitiveSegment[] primitives, long vibrationId)133 public long compose(PrimitiveSegment[] primitives, long vibrationId) { 134 if (mSupportedPrimitives == null) { 135 return 0; 136 } 137 for (PrimitiveSegment primitive : primitives) { 138 if (Arrays.binarySearch(mSupportedPrimitives, primitive.getPrimitiveId()) < 0) { 139 return 0; 140 } 141 } 142 long duration = 0; 143 for (PrimitiveSegment primitive : primitives) { 144 duration += EFFECT_DURATION + primitive.getDelay(); 145 recordEffectSegment(vibrationId, primitive); 146 } 147 applyLatency(mOnLatency); 148 scheduleListener(duration, vibrationId); 149 return duration; 150 } 151 152 @Override composePwle(RampSegment[] primitives, int braking, long vibrationId)153 public long composePwle(RampSegment[] primitives, int braking, long vibrationId) { 154 long duration = 0; 155 for (RampSegment primitive : primitives) { 156 duration += primitive.getDuration(); 157 recordEffectSegment(vibrationId, primitive); 158 } 159 recordBraking(vibrationId, braking); 160 applyLatency(mOnLatency); 161 scheduleListener(duration, vibrationId); 162 return duration; 163 } 164 165 @Override setExternalControl(boolean enabled)166 public void setExternalControl(boolean enabled) { 167 mExternalControlStates.add(enabled); 168 } 169 170 @Override alwaysOnEnable(long id, long effect, long strength)171 public void alwaysOnEnable(long id, long effect, long strength) { 172 PrebakedSegment prebaked = new PrebakedSegment((int) effect, false, (int) strength); 173 mEnabledAlwaysOnEffects.put(id, prebaked); 174 } 175 176 @Override alwaysOnDisable(long id)177 public void alwaysOnDisable(long id) { 178 mEnabledAlwaysOnEffects.remove(id); 179 } 180 181 @Override getInfo(VibratorInfo.Builder infoBuilder)182 public boolean getInfo(VibratorInfo.Builder infoBuilder) { 183 infoBuilder.setCapabilities(mCapabilities); 184 infoBuilder.setSupportedBraking(mSupportedBraking); 185 infoBuilder.setPwleSizeMax(mPwleSizeMax); 186 infoBuilder.setSupportedEffects(mSupportedEffects); 187 if (mSupportedPrimitives != null) { 188 for (int primitive : mSupportedPrimitives) { 189 infoBuilder.setSupportedPrimitive(primitive, EFFECT_DURATION); 190 } 191 } 192 infoBuilder.setCompositionSizeMax(mCompositionSizeMax); 193 infoBuilder.setQFactor(mQFactor); 194 infoBuilder.setFrequencyProfile(new VibratorInfo.FrequencyProfile( 195 mResonantFrequency, mMinFrequency, mFrequencyResolution, mMaxAmplitudes)); 196 return mIsInfoLoadSuccessful; 197 } 198 applyLatency(long latencyMillis)199 private void applyLatency(long latencyMillis) { 200 try { 201 if (latencyMillis > 0) { 202 Thread.sleep(latencyMillis); 203 } 204 } catch (InterruptedException e) { 205 } 206 } 207 scheduleListener(long vibrationDuration, long vibrationId)208 private void scheduleListener(long vibrationDuration, long vibrationId) { 209 mHandler.postDelayed(() -> listener.onComplete(vibratorId, vibrationId), 210 vibrationDuration + mCompletionCallbackDelay); 211 } 212 } 213 FakeVibratorControllerProvider(Looper looper)214 public FakeVibratorControllerProvider(Looper looper) { 215 mHandler = new Handler(looper); 216 mNativeWrapper = new FakeNativeWrapper(); 217 } 218 newVibratorController( int vibratorId, OnVibrationCompleteListener listener)219 public VibratorController newVibratorController( 220 int vibratorId, OnVibrationCompleteListener listener) { 221 return new VibratorController(vibratorId, listener, mNativeWrapper); 222 } 223 224 /** Return {@code true} if this controller was initialized. */ isInitialized()225 public boolean isInitialized() { 226 return mNativeWrapper.isInitialized; 227 } 228 229 /** 230 * Disable fake vibrator hardware, mocking a state where the underlying service is unavailable. 231 */ disableVibrators()232 public void disableVibrators() { 233 mIsAvailable = false; 234 } 235 236 /** 237 * Sets the result for the method that loads the {@link VibratorInfo}, for faking a vibrator 238 * that fails to load some of the hardware data. 239 */ setVibratorInfoLoadSuccessful(boolean successful)240 public void setVibratorInfoLoadSuccessful(boolean successful) { 241 mIsInfoLoadSuccessful = successful; 242 } 243 244 /** 245 * Sets the delay this controller should fake for triggering the vibration completed callback. 246 */ setCompletionCallbackDelay(long millis)247 public void setCompletionCallbackDelay(long millis) { 248 mCompletionCallbackDelay = millis; 249 } 250 251 /** 252 * Sets the latency this controller should fake for turning the vibrator hardware on or setting 253 * the vibration amplitude. 254 */ setOnLatency(long millis)255 public void setOnLatency(long millis) { 256 mOnLatency = millis; 257 } 258 259 /** Sets the latency this controller should fake for turning the vibrator off. */ setOffLatency(long millis)260 public void setOffLatency(long millis) { 261 mOffLatency = millis; 262 } 263 264 /** Set the capabilities of the fake vibrator hardware. */ setCapabilities(int... capabilities)265 public void setCapabilities(int... capabilities) { 266 mCapabilities = Arrays.stream(capabilities).reduce(0, (a, b) -> a | b); 267 } 268 269 /** Set the effects supported by the fake vibrator hardware. */ setSupportedEffects(int... effects)270 public void setSupportedEffects(int... effects) { 271 if (effects != null) { 272 effects = Arrays.copyOf(effects, effects.length); 273 Arrays.sort(effects); 274 } 275 mSupportedEffects = effects; 276 } 277 278 /** Set the effects supported by the fake vibrator hardware. */ setSupportedBraking(int... braking)279 public void setSupportedBraking(int... braking) { 280 if (braking != null) { 281 braking = Arrays.copyOf(braking, braking.length); 282 Arrays.sort(braking); 283 } 284 mSupportedBraking = braking; 285 } 286 287 /** Set the primitives supported by the fake vibrator hardware. */ setSupportedPrimitives(int... primitives)288 public void setSupportedPrimitives(int... primitives) { 289 if (primitives != null) { 290 primitives = Arrays.copyOf(primitives, primitives.length); 291 Arrays.sort(primitives); 292 } 293 mSupportedPrimitives = primitives; 294 } 295 296 /** Set the max number of primitives allowed in a composition by the fake vibrator hardware. */ setCompositionSizeMax(int compositionSizeMax)297 public void setCompositionSizeMax(int compositionSizeMax) { 298 mCompositionSizeMax = compositionSizeMax; 299 } 300 301 /** Set the max number of PWLEs allowed in a composition by the fake vibrator hardware. */ setPwleSizeMax(int pwleSizeMax)302 public void setPwleSizeMax(int pwleSizeMax) { 303 mPwleSizeMax = pwleSizeMax; 304 } 305 306 /** Set the resonant frequency of the fake vibrator hardware. */ setResonantFrequency(float frequencyHz)307 public void setResonantFrequency(float frequencyHz) { 308 mResonantFrequency = frequencyHz; 309 } 310 311 /** Set the minimum frequency of the fake vibrator hardware. */ setMinFrequency(float frequencyHz)312 public void setMinFrequency(float frequencyHz) { 313 mMinFrequency = frequencyHz; 314 } 315 316 /** Set the frequency resolution of the fake vibrator hardware. */ setFrequencyResolution(float frequencyHz)317 public void setFrequencyResolution(float frequencyHz) { 318 mFrequencyResolution = frequencyHz; 319 } 320 321 /** Set the Q factor of the fake vibrator hardware. */ setQFactor(float qFactor)322 public void setQFactor(float qFactor) { 323 mQFactor = qFactor; 324 } 325 326 /** Set the max amplitude supported for each frequency f the fake vibrator hardware. */ setMaxAmplitudes(float... maxAmplitudes)327 public void setMaxAmplitudes(float... maxAmplitudes) { 328 mMaxAmplitudes = maxAmplitudes; 329 } 330 331 /** 332 * Return the amplitudes set by this controller, including zeroes for each time the vibrator was 333 * turned off. 334 */ getAmplitudes()335 public List<Float> getAmplitudes() { 336 return new ArrayList<>(mAmplitudes); 337 } 338 339 /** Return the braking values passed to the compose PWLE method. */ getBraking(long vibrationId)340 public List<Integer> getBraking(long vibrationId) { 341 if (mBraking.containsKey(vibrationId)) { 342 return new ArrayList<>(mBraking.get(vibrationId)); 343 } else { 344 return new ArrayList<>(); 345 } 346 } 347 348 /** Return list of {@link VibrationEffectSegment} played by this controller, in order. */ getEffectSegments(long vibrationId)349 public List<VibrationEffectSegment> getEffectSegments(long vibrationId) { 350 if (mEffectSegments.containsKey(vibrationId)) { 351 return new ArrayList<>(mEffectSegments.get(vibrationId)); 352 } else { 353 return new ArrayList<>(); 354 } 355 } 356 357 /** 358 * Returns a list of all vibrations' effect segments, for external-use where vibration IDs 359 * aren't exposed. 360 */ getAllEffectSegments()361 public List<VibrationEffectSegment> getAllEffectSegments() { 362 // Returns segments in order of vibrationId, which increases over time. TreeMap gives order. 363 ArrayList<VibrationEffectSegment> result = new ArrayList<>(); 364 for (List<VibrationEffectSegment> subList : mEffectSegments.values()) { 365 result.addAll(subList); 366 } 367 return result; 368 } 369 /** Return list of states set for external control to the fake vibrator hardware. */ getExternalControlStates()370 public List<Boolean> getExternalControlStates() { 371 return mExternalControlStates; 372 } 373 374 /** Returns the number of times the vibrator was turned off. */ getOffCount()375 public int getOffCount() { 376 return mOffCount; 377 } 378 379 /** 380 * Return the {@link PrebakedSegment} effect enabled with given id, or {@code null} if 381 * missing or disabled. 382 */ 383 @Nullable getAlwaysOnEffect(int id)384 public PrebakedSegment getAlwaysOnEffect(int id) { 385 return mEnabledAlwaysOnEffects.get((long) id); 386 } 387 } 388