• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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