• 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 android.os;
18 
19 import android.annotation.FloatRange;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.hardware.vibrator.Braking;
23 import android.hardware.vibrator.IVibrator;
24 import android.util.Log;
25 import android.util.MathUtils;
26 import android.util.Range;
27 import android.util.SparseBooleanArray;
28 import android.util.SparseIntArray;
29 
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.List;
33 import java.util.Objects;
34 
35 /**
36  * A VibratorInfo describes the capabilities of a {@link Vibrator}.
37  *
38  * This description includes its capabilities, list of supported effects and composition primitives.
39  *
40  * @hide
41  */
42 public class VibratorInfo implements Parcelable {
43     private static final String TAG = "VibratorInfo";
44 
45     /** @hide */
46     public static final VibratorInfo EMPTY_VIBRATOR_INFO = new VibratorInfo.Builder(-1).build();
47 
48     private final int mId;
49     private final long mCapabilities;
50     @Nullable
51     private final SparseBooleanArray mSupportedEffects;
52     @Nullable
53     private final SparseBooleanArray mSupportedBraking;
54     private final SparseIntArray mSupportedPrimitives;
55     private final int mPrimitiveDelayMax;
56     private final int mCompositionSizeMax;
57     private final int mPwlePrimitiveDurationMax;
58     private final int mPwleSizeMax;
59     private final float mQFactor;
60     private final FrequencyMapping mFrequencyMapping;
61 
VibratorInfo(Parcel in)62     VibratorInfo(Parcel in) {
63         mId = in.readInt();
64         mCapabilities = in.readLong();
65         mSupportedEffects = in.readSparseBooleanArray();
66         mSupportedBraking = in.readSparseBooleanArray();
67         mSupportedPrimitives = in.readSparseIntArray();
68         mPrimitiveDelayMax = in.readInt();
69         mCompositionSizeMax = in.readInt();
70         mPwlePrimitiveDurationMax = in.readInt();
71         mPwleSizeMax = in.readInt();
72         mQFactor = in.readFloat();
73         mFrequencyMapping = in.readParcelable(VibratorInfo.class.getClassLoader());
74     }
75 
76     /**
77      * Default constructor.
78      *
79      * @param id                       The vibrator id.
80      * @param capabilities             All capability flags of the vibrator, defined in
81      *                                 IVibrator.CAP_*.
82      * @param supportedEffects         All supported predefined effects, enum values from
83      *                                 {@link android.hardware.vibrator.Effect}.
84      * @param supportedBraking         All supported braking types, enum values from {@link
85      *                                 Braking}.
86      * @param supportedPrimitives      All supported primitive effects, key are enum values from
87      *                                 {@link android.hardware.vibrator.CompositePrimitive} and
88      *                                 values are estimated durations in milliseconds.
89      * @param primitiveDelayMax        The maximum delay that can be set to a composition primitive
90      *                                 in milliseconds.
91      * @param compositionSizeMax       The maximum number of primitives supported by a composition.
92      * @param pwlePrimitiveDurationMax The maximum duration of a PWLE primitive in milliseconds.
93      * @param pwleSizeMax              The maximum number of primitives supported by a PWLE
94      *                                 composition.
95      * @param qFactor                  The vibrator quality factor.
96      * @param frequencyMapping         The description of the vibrator supported frequencies and max
97      *                                 amplitude mappings.
98      * @hide
99      */
VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects, @Nullable SparseBooleanArray supportedBraking, @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax, int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax, float qFactor, @NonNull FrequencyMapping frequencyMapping)100     public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
101             @Nullable SparseBooleanArray supportedBraking,
102             @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
103             int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
104             float qFactor, @NonNull FrequencyMapping frequencyMapping) {
105         mId = id;
106         mCapabilities = capabilities;
107         mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
108         mSupportedBraking = supportedBraking == null ? null : supportedBraking.clone();
109         mSupportedPrimitives = supportedPrimitives.clone();
110         mPrimitiveDelayMax = primitiveDelayMax;
111         mCompositionSizeMax = compositionSizeMax;
112         mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
113         mPwleSizeMax = pwleSizeMax;
114         mQFactor = qFactor;
115         mFrequencyMapping = frequencyMapping;
116     }
117 
VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator)118     protected VibratorInfo(int id, int capabilities, VibratorInfo baseVibrator) {
119         this(id, capabilities, baseVibrator.mSupportedEffects, baseVibrator.mSupportedBraking,
120                 baseVibrator.mSupportedPrimitives, baseVibrator.mPrimitiveDelayMax,
121                 baseVibrator.mCompositionSizeMax, baseVibrator.mPwlePrimitiveDurationMax,
122                 baseVibrator.mPwleSizeMax, baseVibrator.mQFactor, baseVibrator.mFrequencyMapping);
123     }
124 
125     @Override
writeToParcel(Parcel dest, int flags)126     public void writeToParcel(Parcel dest, int flags) {
127         dest.writeInt(mId);
128         dest.writeLong(mCapabilities);
129         dest.writeSparseBooleanArray(mSupportedEffects);
130         dest.writeSparseBooleanArray(mSupportedBraking);
131         dest.writeSparseIntArray(mSupportedPrimitives);
132         dest.writeInt(mPrimitiveDelayMax);
133         dest.writeInt(mCompositionSizeMax);
134         dest.writeInt(mPwlePrimitiveDurationMax);
135         dest.writeInt(mPwleSizeMax);
136         dest.writeFloat(mQFactor);
137         dest.writeParcelable(mFrequencyMapping, flags);
138     }
139 
140     @Override
describeContents()141     public int describeContents() {
142         return 0;
143     }
144 
145     @Override
equals(Object o)146     public boolean equals(Object o) {
147         if (this == o) {
148             return true;
149         }
150         if (!(o instanceof VibratorInfo)) {
151             return false;
152         }
153         VibratorInfo that = (VibratorInfo) o;
154         int supportedPrimitivesCount = mSupportedPrimitives.size();
155         if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) {
156             return false;
157         }
158         for (int i = 0; i < supportedPrimitivesCount; i++) {
159             if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
160                 return false;
161             }
162             if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
163                 return false;
164             }
165         }
166         return mId == that.mId && mCapabilities == that.mCapabilities
167                 && mPrimitiveDelayMax == that.mPrimitiveDelayMax
168                 && mCompositionSizeMax == that.mCompositionSizeMax
169                 && mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax
170                 && mPwleSizeMax == that.mPwleSizeMax
171                 && Objects.equals(mSupportedEffects, that.mSupportedEffects)
172                 && Objects.equals(mSupportedBraking, that.mSupportedBraking)
173                 && Objects.equals(mQFactor, that.mQFactor)
174                 && Objects.equals(mFrequencyMapping, that.mFrequencyMapping);
175     }
176 
177     @Override
hashCode()178     public int hashCode() {
179         int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
180                 mQFactor, mFrequencyMapping);
181         for (int i = 0; i < mSupportedPrimitives.size(); i++) {
182             hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
183             hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
184         }
185         return hashCode;
186     }
187 
188     @Override
toString()189     public String toString() {
190         return "VibratorInfo{"
191                 + "mId=" + mId
192                 + ", mCapabilities=" + Arrays.toString(getCapabilitiesNames())
193                 + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities)
194                 + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
195                 + ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
196                 + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
197                 + ", mPrimitiveDelayMax=" + mPrimitiveDelayMax
198                 + ", mCompositionSizeMax=" + mCompositionSizeMax
199                 + ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
200                 + ", mPwleSizeMax=" + mPwleSizeMax
201                 + ", mQFactor=" + mQFactor
202                 + ", mFrequencyMapping=" + mFrequencyMapping
203                 + '}';
204     }
205 
206     /** Return the id of this vibrator. */
getId()207     public int getId() {
208         return mId;
209     }
210 
211     /**
212      * Check whether the vibrator has amplitude control.
213      *
214      * @return True if the hardware can control the amplitude of the vibrations, otherwise false.
215      */
hasAmplitudeControl()216     public boolean hasAmplitudeControl() {
217         return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
218     }
219 
220     /**
221      * Returns a default value to be applied to composed PWLE effects for braking.
222      *
223      * @return a supported braking value, one of android.hardware.vibrator.Braking.*
224      * @hide
225      */
getDefaultBraking()226     public int getDefaultBraking() {
227         if (mSupportedBraking != null) {
228             int size = mSupportedBraking.size();
229             for (int i = 0; i < size; i++) {
230                 if (mSupportedBraking.keyAt(i) != Braking.NONE) {
231                     return mSupportedBraking.keyAt(i);
232                 }
233             }
234         }
235         return Braking.NONE;
236     }
237 
238     /**
239      * Query whether the vibrator supports the given effect.
240      *
241      * @param effectId Which effects to query for.
242      * @return {@link Vibrator#VIBRATION_EFFECT_SUPPORT_YES} if the effect is supported,
243      * {@link Vibrator#VIBRATION_EFFECT_SUPPORT_NO} if it isn't supported, or
244      * {@link Vibrator#VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether it's
245      * supported or not.
246      */
247     @Vibrator.VibrationEffectSupport
isEffectSupported(@ibrationEffect.EffectType int effectId)248     public int isEffectSupported(@VibrationEffect.EffectType int effectId) {
249         if (mSupportedEffects == null) {
250             return Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN;
251         }
252         return mSupportedEffects.get(effectId) ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES
253                 : Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
254     }
255 
256     /**
257      * Query whether the vibrator supports the given primitive.
258      *
259      * @param primitiveId Which primitives to query for.
260      * @return Whether the primitive is supported.
261      */
isPrimitiveSupported( @ibrationEffect.Composition.PrimitiveType int primitiveId)262     public boolean isPrimitiveSupported(
263             @VibrationEffect.Composition.PrimitiveType int primitiveId) {
264         return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)
265                 && (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
266     }
267 
268     /**
269      * Query the estimated duration of given primitive.
270      *
271      * @param primitiveId Which primitives to query for.
272      * @return The duration in milliseconds estimated for the primitive, or zero if primitive not
273      * supported.
274      */
getPrimitiveDuration( @ibrationEffect.Composition.PrimitiveType int primitiveId)275     public int getPrimitiveDuration(
276             @VibrationEffect.Composition.PrimitiveType int primitiveId) {
277         return mSupportedPrimitives.get(primitiveId);
278     }
279 
280     /**
281      * Query the maximum delay supported for a primitive in a composed effect.
282      *
283      * @return The max delay in milliseconds, or zero if unlimited.
284      */
getPrimitiveDelayMax()285     public int getPrimitiveDelayMax() {
286         return mPrimitiveDelayMax;
287     }
288 
289     /**
290      * Query the maximum number of primitives supported in a composed effect.
291      *
292      * @return The max number of primitives supported, or zero if unlimited.
293      */
getCompositionSizeMax()294     public int getCompositionSizeMax() {
295         return mCompositionSizeMax;
296     }
297 
298     /**
299      * Query the maximum duration supported for a primitive in a PWLE composition.
300      *
301      * @return The max duration in milliseconds, or zero if unlimited.
302      */
getPwlePrimitiveDurationMax()303     public int getPwlePrimitiveDurationMax() {
304         return mPwlePrimitiveDurationMax;
305     }
306 
307     /**
308      * Query the maximum number of primitives supported in a PWLE composition.
309      *
310      * @return The max number of primitives supported, or zero if unlimited.
311      */
getPwleSizeMax()312     public int getPwleSizeMax() {
313         return mPwleSizeMax;
314     }
315 
316     /**
317      * Check against this vibrator capabilities.
318      *
319      * @param capability one of IVibrator.CAP_*
320      * @return true if this vibrator has this capability, false otherwise
321      * @hide
322      */
hasCapability(long capability)323     public boolean hasCapability(long capability) {
324         return (mCapabilities & capability) == capability;
325     }
326 
327     /**
328      * Gets the resonant frequency of the vibrator.
329      *
330      * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
331      *         this vibrator is a composite of multiple physical devices.
332      */
getResonantFrequency()333     public float getResonantFrequency() {
334         return mFrequencyMapping.mResonantFrequencyHz;
335     }
336 
337     /**
338      * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
339      *
340      * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
341      *         this vibrator is a composite of multiple physical devices.
342      */
getQFactor()343     public float getQFactor() {
344         return mQFactor;
345     }
346 
347     /**
348      * Return a range of relative frequency values supported by the vibrator.
349      *
350      * @return A range of relative frequency values supported. The range will always contain the
351      * value 0, representing the device resonant frequency. Devices without frequency control will
352      * return the range [0,0]. Devices with frequency control will always return a range containing
353      * the safe range [-1, 1].
354      * @hide
355      */
getFrequencyRange()356     public Range<Float> getFrequencyRange() {
357         return mFrequencyMapping.mRelativeFrequencyRange;
358     }
359 
360     /**
361      * Return the maximum amplitude the vibrator can play at given relative frequency.
362      *
363      * @return a value in [0,1] representing the maximum amplitude the device can play at given
364      * relative frequency. Devices without frequency control will return 1 for the input zero
365      * (resonant frequency), and 0 to any other input. Devices with frequency control will return
366      * the supported value, for input in {@code #getFrequencyRange()}, and 0 for any other input.
367      * @hide
368      */
369     @FloatRange(from = 0, to = 1)
getMaxAmplitude(float relativeFrequency)370     public float getMaxAmplitude(float relativeFrequency) {
371         if (mFrequencyMapping.isEmpty()) {
372             // The vibrator has not provided values for frequency mapping.
373             // Return the expected behavior for devices without frequency control.
374             return Float.compare(relativeFrequency, 0) == 0 ? 1 : 0;
375         }
376         return mFrequencyMapping.getMaxAmplitude(relativeFrequency);
377     }
378 
379     /**
380      * Return absolute frequency value for this vibrator, in hertz, that corresponds to given
381      * relative frequency.
382      *
383      * @retur a value in hertz that corresponds to given relative frequency. Input values outside
384      * {@link #getFrequencyRange()} will return {@link Float#NaN}. Devices without frequency control
385      * will return {@link Float#NaN} for any input.
386      * @hide
387      */
388     @FloatRange(from = 0)
getAbsoluteFrequency(float relativeFrequency)389     public float getAbsoluteFrequency(float relativeFrequency) {
390         return mFrequencyMapping.toHertz(relativeFrequency);
391     }
392 
getCapabilities()393     protected long getCapabilities() {
394         return mCapabilities;
395     }
396 
getCapabilitiesNames()397     private String[] getCapabilitiesNames() {
398         List<String> names = new ArrayList<>();
399         if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
400             names.add("ON_CALLBACK");
401         }
402         if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
403             names.add("PERFORM_CALLBACK");
404         }
405         if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
406             names.add("COMPOSE_EFFECTS");
407         }
408         if (hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
409             names.add("COMPOSE_PWLE_EFFECTS");
410         }
411         if (hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
412             names.add("ALWAYS_ON_CONTROL");
413         }
414         if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
415             names.add("AMPLITUDE_CONTROL");
416         }
417         if (hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)) {
418             names.add("FREQUENCY_CONTROL");
419         }
420         if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
421             names.add("EXTERNAL_CONTROL");
422         }
423         if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
424             names.add("EXTERNAL_AMPLITUDE_CONTROL");
425         }
426         return names.toArray(new String[names.size()]);
427     }
428 
getSupportedEffectsNames()429     private String[] getSupportedEffectsNames() {
430         if (mSupportedEffects == null) {
431             return new String[0];
432         }
433         String[] names = new String[mSupportedEffects.size()];
434         for (int i = 0; i < mSupportedEffects.size(); i++) {
435             names[i] = VibrationEffect.effectIdToString(mSupportedEffects.keyAt(i));
436         }
437         return names;
438     }
439 
getSupportedBrakingNames()440     private String[] getSupportedBrakingNames() {
441         if (mSupportedBraking == null) {
442             return new String[0];
443         }
444         String[] names = new String[mSupportedBraking.size()];
445         for (int i = 0; i < mSupportedBraking.size(); i++) {
446             switch (mSupportedBraking.keyAt(i)) {
447                 case Braking.NONE:
448                     names[i] = "NONE";
449                     break;
450                 case Braking.CLAB:
451                     names[i] = "CLAB";
452                     break;
453                 default:
454                     names[i] = Integer.toString(mSupportedBraking.keyAt(i));
455             }
456         }
457         return names;
458     }
459 
getSupportedPrimitivesNames()460     private String[] getSupportedPrimitivesNames() {
461         int supportedPrimitivesCount = mSupportedPrimitives.size();
462         String[] names = new String[supportedPrimitivesCount];
463         for (int i = 0; i < supportedPrimitivesCount; i++) {
464             names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i));
465         }
466         return names;
467     }
468 
469     /**
470      * Describes how frequency should be mapped to absolute values for a specific {@link Vibrator}.
471      *
472      * <p>This mapping is defined by the following parameters:
473      *
474      * <ol>
475      *     <li>{@code minFrequency}, {@code resonantFrequency} and {@code frequencyResolution}, in
476      *         hertz, provided by the vibrator.
477      *     <li>{@code maxAmplitudes} a list of values in [0,1] provided by the vibrator, where
478      *         {@code maxAmplitudes[i]} represents max supported amplitude at frequency
479      *         {@code minFrequency + frequencyResolution * i}.
480      *     <li>{@code maxFrequency = minFrequency + frequencyResolution * (maxAmplitudes.length-1)}
481      *     <li>{@code suggestedSafeRangeHz} is the suggested frequency range in hertz that should be
482      *         mapped to relative values -1 and 1, where 0 maps to {@code resonantFrequency}.
483      * </ol>
484      *
485      * <p>The mapping is defined linearly by the following points:
486      *
487      * <ol>
488      *     <li>{@code toHertz(relativeMinFrequency) = minFrequency}
489      *     <li>{@code                   toHertz(-1) = resonantFrequency - safeRange / 2}
490      *     <li>{@code                    toHertz(0) = resonantFrequency}
491      *     <li>{@code                    toHertz(1) = resonantFrequency + safeRange / 2}
492      *     <li>{@code toHertz(relativeMaxFrequency) = maxFrequency}
493      * </ol>
494      *
495      * @hide
496      */
497     public static final class FrequencyMapping implements Parcelable {
498         private final float mMinFrequencyHz;
499         private final float mResonantFrequencyHz;
500         private final float mFrequencyResolutionHz;
501         private final float mSuggestedSafeRangeHz;
502         private final float[] mMaxAmplitudes;
503 
504         // Relative fields calculated from input values:
505         private final Range<Float> mRelativeFrequencyRange;
506 
FrequencyMapping(Parcel in)507         FrequencyMapping(Parcel in) {
508             this(in.readFloat(), in.readFloat(), in.readFloat(), in.readFloat(),
509                     in.createFloatArray());
510         }
511 
512         /**
513          * Default constructor.
514          *
515          * @param minFrequencyHz        Minimum supported frequency, in hertz.
516          * @param resonantFrequencyHz   The vibrator resonant frequency, in hertz.
517          * @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
518          *                              amplitudes mapping.
519          * @param suggestedSafeRangeHz  The suggested range, in hertz, for the safe relative
520          *                              frequency range represented by [-1, 1].
521          * @param maxAmplitudes         The max amplitude supported by each supported frequency,
522          *                              starting at minimum frequency with jumps of frequency
523          *                              resolution.
524          * @hide
525          */
FrequencyMapping(float minFrequencyHz, float resonantFrequencyHz, float frequencyResolutionHz, float suggestedSafeRangeHz, float[] maxAmplitudes)526         public FrequencyMapping(float minFrequencyHz, float resonantFrequencyHz,
527                 float frequencyResolutionHz, float suggestedSafeRangeHz, float[] maxAmplitudes) {
528             mMinFrequencyHz = minFrequencyHz;
529             mResonantFrequencyHz = resonantFrequencyHz;
530             mFrequencyResolutionHz = frequencyResolutionHz;
531             mSuggestedSafeRangeHz = suggestedSafeRangeHz;
532             mMaxAmplitudes = new float[maxAmplitudes == null ? 0 : maxAmplitudes.length];
533             if (maxAmplitudes != null) {
534                 System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length);
535             }
536 
537             float maxFrequencyHz =
538                     minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1);
539             if (Float.isNaN(resonantFrequencyHz) || Float.isNaN(minFrequencyHz)
540                     || Float.isNaN(frequencyResolutionHz) || Float.isNaN(suggestedSafeRangeHz)
541                     || resonantFrequencyHz < minFrequencyHz
542                     || resonantFrequencyHz > maxFrequencyHz) {
543                 // Some required fields are undefined or have bad values.
544                 // Leave this mapping empty.
545                 mRelativeFrequencyRange = Range.create(0f, 0f);
546                 return;
547             }
548 
549             // Calculate actual safe range, limiting the suggested one by the device supported range
550             float safeDelta = MathUtils.min(
551                     suggestedSafeRangeHz / 2,
552                     resonantFrequencyHz - minFrequencyHz,
553                     maxFrequencyHz - resonantFrequencyHz);
554             mRelativeFrequencyRange = Range.create(
555                     (minFrequencyHz - resonantFrequencyHz) / safeDelta,
556                     (maxFrequencyHz - resonantFrequencyHz) / safeDelta);
557         }
558 
559         /**
560          * Returns true if this frequency mapping is empty, i.e. the only supported relative
561          * frequency is 0 (resonant frequency).
562          */
isEmpty()563         public boolean isEmpty() {
564             return Float.compare(mRelativeFrequencyRange.getLower(),
565                     mRelativeFrequencyRange.getUpper()) == 0;
566         }
567 
568         /**
569          * Returns the frequency value in hertz that is mapped to the given relative frequency.
570          *
571          * @return The mapped frequency, in hertz, or {@link Float#NaN} is value outside the device
572          * supported range.
573          */
toHertz(float relativeFrequency)574         public float toHertz(float relativeFrequency) {
575             if (!mRelativeFrequencyRange.contains(relativeFrequency)) {
576                 return Float.NaN;
577             }
578             float relativeMinFrequency = mRelativeFrequencyRange.getLower();
579             if (Float.compare(relativeMinFrequency, 0) == 0) {
580                 // relative supported range is [0,0], so toHertz(0) should be the resonant frequency
581                 return mResonantFrequencyHz;
582             }
583             float shift = (mMinFrequencyHz - mResonantFrequencyHz) / relativeMinFrequency;
584             return mResonantFrequencyHz + relativeFrequency * shift;
585         }
586 
587         /**
588          * Returns the maximum amplitude the vibrator can reach while playing at given relative
589          * frequency.
590          *
591          * @return A value in [0,1] representing the max amplitude supported at given relative
592          * frequency. This will return 0 if frequency is outside supported range, or if max
593          * amplitude mapping is empty.
594          */
getMaxAmplitude(float relativeFrequency)595         public float getMaxAmplitude(float relativeFrequency) {
596             float frequencyHz = toHertz(relativeFrequency);
597             if (Float.isNaN(frequencyHz)) {
598                 // Unsupported frequency requested, vibrator cannot play at this frequency.
599                 return 0;
600             }
601             float position = (frequencyHz - mMinFrequencyHz) / mFrequencyResolutionHz;
602             int floorIndex = (int) Math.floor(position);
603             int ceilIndex = (int) Math.ceil(position);
604             if (floorIndex < 0 || floorIndex >= mMaxAmplitudes.length) {
605                 if (mMaxAmplitudes.length > 0) {
606                     // This should never happen if the setup of relative frequencies was correct.
607                     Log.w(TAG, "Max amplitudes has " + mMaxAmplitudes.length
608                             + " entries and was expected to cover the frequency " + frequencyHz
609                             + " Hz when starting at min frequency of " + mMinFrequencyHz
610                             + " Hz with resolution of " + mFrequencyResolutionHz + " Hz.");
611                 }
612                 return 0;
613             }
614             if (floorIndex != ceilIndex && ceilIndex < mMaxAmplitudes.length) {
615                 // Value in between two mapped frequency values, use the lowest supported one.
616                 return MathUtils.min(mMaxAmplitudes[floorIndex], mMaxAmplitudes[ceilIndex]);
617             }
618             return mMaxAmplitudes[floorIndex];
619         }
620 
621         @Override
writeToParcel(Parcel dest, int flags)622         public void writeToParcel(Parcel dest, int flags) {
623             dest.writeFloat(mMinFrequencyHz);
624             dest.writeFloat(mResonantFrequencyHz);
625             dest.writeFloat(mFrequencyResolutionHz);
626             dest.writeFloat(mSuggestedSafeRangeHz);
627             dest.writeFloatArray(mMaxAmplitudes);
628         }
629 
630         @Override
describeContents()631         public int describeContents() {
632             return 0;
633         }
634 
635         @Override
equals(Object o)636         public boolean equals(Object o) {
637             if (this == o) {
638                 return true;
639             }
640             if (!(o instanceof FrequencyMapping)) {
641                 return false;
642             }
643             FrequencyMapping that = (FrequencyMapping) o;
644             return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0
645                     && Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
646                     && Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0
647                     && Float.compare(mSuggestedSafeRangeHz, that.mSuggestedSafeRangeHz) == 0
648                     && Arrays.equals(mMaxAmplitudes, that.mMaxAmplitudes);
649         }
650 
651         @Override
hashCode()652         public int hashCode() {
653             int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz,
654                     mFrequencyResolutionHz, mSuggestedSafeRangeHz);
655             hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes);
656             return hashCode;
657         }
658 
659         @Override
toString()660         public String toString() {
661             return "FrequencyMapping{"
662                     + "mRelativeFrequencyRange=" + mRelativeFrequencyRange
663                     + ", mMinFrequency=" + mMinFrequencyHz
664                     + ", mResonantFrequency=" + mResonantFrequencyHz
665                     + ", mMaxFrequency="
666                     + (mMinFrequencyHz + mFrequencyResolutionHz * (mMaxAmplitudes.length - 1))
667                     + ", mFrequencyResolution=" + mFrequencyResolutionHz
668                     + ", mSuggestedSafeRange=" + mSuggestedSafeRangeHz
669                     + ", mMaxAmplitudes count=" + mMaxAmplitudes.length
670                     + '}';
671         }
672 
673         @NonNull
674         public static final Creator<FrequencyMapping> CREATOR =
675                 new Creator<FrequencyMapping>() {
676                     @Override
677                     public FrequencyMapping createFromParcel(Parcel in) {
678                         return new FrequencyMapping(in);
679                     }
680 
681                     @Override
682                     public FrequencyMapping[] newArray(int size) {
683                         return new FrequencyMapping[size];
684                     }
685                 };
686     }
687 
688     /** @hide */
689     public static final class Builder {
690         private final int mId;
691         private long mCapabilities;
692         private SparseBooleanArray mSupportedEffects;
693         private SparseBooleanArray mSupportedBraking;
694         private SparseIntArray mSupportedPrimitives = new SparseIntArray();
695         private int mPrimitiveDelayMax;
696         private int mCompositionSizeMax;
697         private int mPwlePrimitiveDurationMax;
698         private int mPwleSizeMax;
699         private float mQFactor = Float.NaN;
700         private FrequencyMapping mFrequencyMapping =
701                 new FrequencyMapping(Float.NaN, Float.NaN, Float.NaN, Float.NaN, null);
702 
703         /** A builder class for a {@link VibratorInfo}. */
Builder(int id)704         public Builder(int id) {
705             mId = id;
706         }
707 
708         /** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
709         @NonNull
setCapabilities(long capabilities)710         public Builder setCapabilities(long capabilities) {
711             mCapabilities = capabilities;
712             return this;
713         }
714 
715         /** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
716         @NonNull
setSupportedEffects(int... supportedEffects)717         public Builder setSupportedEffects(int... supportedEffects) {
718             mSupportedEffects = toSparseBooleanArray(supportedEffects);
719             return this;
720         }
721 
722         /** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
723         @NonNull
setSupportedBraking(int... supportedBraking)724         public Builder setSupportedBraking(int... supportedBraking) {
725             mSupportedBraking = toSparseBooleanArray(supportedBraking);
726             return this;
727         }
728 
729         /** Configure maximum duration, in milliseconds, of a PWLE primitive. */
730         @NonNull
setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax)731         public Builder setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) {
732             mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
733             return this;
734         }
735 
736         /** Configure maximum number of primitives supported in a single PWLE composed effect. */
737         @NonNull
setPwleSizeMax(int pwleSizeMax)738         public Builder setPwleSizeMax(int pwleSizeMax) {
739             mPwleSizeMax = pwleSizeMax;
740             return this;
741         }
742 
743         /** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
744         @NonNull
setSupportedPrimitive(int primitiveId, int duration)745         public Builder setSupportedPrimitive(int primitiveId, int duration) {
746             mSupportedPrimitives.put(primitiveId, duration);
747             return this;
748         }
749 
750         /** Configure maximum delay, in milliseconds, supported in a composed effect primitive. */
751         @NonNull
setPrimitiveDelayMax(int primitiveDelayMax)752         public Builder setPrimitiveDelayMax(int primitiveDelayMax) {
753             mPrimitiveDelayMax = primitiveDelayMax;
754             return this;
755         }
756 
757         /** Configure maximum number of primitives supported in a single composed effect. */
758         @NonNull
setCompositionSizeMax(int compositionSizeMax)759         public Builder setCompositionSizeMax(int compositionSizeMax) {
760             mCompositionSizeMax = compositionSizeMax;
761             return this;
762         }
763 
764         /** Configure the vibrator quality factor. */
765         @NonNull
setQFactor(float qFactor)766         public Builder setQFactor(float qFactor) {
767             mQFactor = qFactor;
768             return this;
769         }
770 
771         /** Configure the vibrator frequency information like resonant frequency and bandwidth. */
772         @NonNull
setFrequencyMapping(FrequencyMapping frequencyMapping)773         public Builder setFrequencyMapping(FrequencyMapping frequencyMapping) {
774             mFrequencyMapping = frequencyMapping;
775             return this;
776         }
777 
778         /** Build the configured {@link VibratorInfo}. */
779         @NonNull
build()780         public VibratorInfo build() {
781             return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
782                     mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
783                     mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyMapping);
784         }
785 
786         /**
787          * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is
788          * mapped
789          * to {@code true}.
790          */
791         @Nullable
toSparseBooleanArray(int[] supportedKeys)792         private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
793             if (supportedKeys == null) {
794                 return null;
795             }
796             SparseBooleanArray array = new SparseBooleanArray();
797             for (int key : supportedKeys) {
798                 array.put(key, true);
799             }
800             return array;
801         }
802     }
803 
804     @NonNull
805     public static final Creator<VibratorInfo> CREATOR =
806             new Creator<VibratorInfo>() {
807                 @Override
808                 public VibratorInfo createFromParcel(Parcel in) {
809                     return new VibratorInfo(in);
810                 }
811 
812                 @Override
813                 public VibratorInfo[] newArray(int size) {
814                     return new VibratorInfo[size];
815                 }
816             };
817 }
818