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