• 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.NonNull;
20 import android.annotation.Nullable;
21 import android.hardware.vibrator.Braking;
22 import android.hardware.vibrator.IVibrator;
23 import android.os.vibrator.Flags;
24 import android.util.IndentingPrintWriter;
25 import android.util.MathUtils;
26 import android.util.Pair;
27 import android.util.Range;
28 import android.util.SparseBooleanArray;
29 import android.util.SparseIntArray;
30 
31 import com.android.internal.util.Preconditions;
32 
33 import java.util.ArrayList;
34 import java.util.Arrays;
35 import java.util.Comparator;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Objects;
39 import java.util.TreeMap;
40 
41 /**
42  * A VibratorInfo describes the capabilities of a {@link Vibrator}.
43  *
44  * <p>This description includes its capabilities, list of supported effects and composition
45  * primitives.
46  *
47  * @hide
48  */
49 public class VibratorInfo implements Parcelable {
50     private static final String TAG = "VibratorInfo";
51 
52     /** @hide */
53     public static final VibratorInfo EMPTY_VIBRATOR_INFO = new VibratorInfo.Builder(-1).build();
54 
55     private final int mId;
56     private final long mCapabilities;
57     @Nullable
58     private final SparseBooleanArray mSupportedEffects;
59     @Nullable
60     private final SparseBooleanArray mSupportedBraking;
61     private final SparseIntArray mSupportedPrimitives;
62     private final int mPrimitiveDelayMax;
63     private final int mCompositionSizeMax;
64     private final int mPwlePrimitiveDurationMax;
65     private final int mPwleSizeMax;
66     private final float mQFactor;
67     private final FrequencyProfileLegacy mFrequencyProfileLegacy;
68     private final FrequencyProfile mFrequencyProfile;
69     private final int mMaxEnvelopeEffectSize;
70     private final int mMinEnvelopeEffectControlPointDurationMillis;
71     private final int mMaxEnvelopeEffectControlPointDurationMillis;
72 
VibratorInfo(Parcel in)73     VibratorInfo(Parcel in) {
74         mId = in.readInt();
75         mCapabilities = in.readLong();
76         mSupportedEffects = in.readSparseBooleanArray();
77         mSupportedBraking = in.readSparseBooleanArray();
78         mSupportedPrimitives = in.readSparseIntArray();
79         mPrimitiveDelayMax = in.readInt();
80         mCompositionSizeMax = in.readInt();
81         mPwlePrimitiveDurationMax = in.readInt();
82         mPwleSizeMax = in.readInt();
83         mQFactor = in.readFloat();
84         mFrequencyProfileLegacy = FrequencyProfileLegacy.CREATOR.createFromParcel(in);
85         mFrequencyProfile = FrequencyProfile.CREATOR.createFromParcel(in);
86         mMaxEnvelopeEffectSize = in.readInt();
87         mMinEnvelopeEffectControlPointDurationMillis = in.readInt();
88         mMaxEnvelopeEffectControlPointDurationMillis = in.readInt();
89     }
90 
VibratorInfo(int id, @NonNull VibratorInfo baseVibratorInfo)91     public VibratorInfo(int id, @NonNull VibratorInfo baseVibratorInfo) {
92         this(id, baseVibratorInfo.mCapabilities, baseVibratorInfo.mSupportedEffects,
93                 baseVibratorInfo.mSupportedBraking, baseVibratorInfo.mSupportedPrimitives,
94                 baseVibratorInfo.mPrimitiveDelayMax, baseVibratorInfo.mCompositionSizeMax,
95                 baseVibratorInfo.mPwlePrimitiveDurationMax, baseVibratorInfo.mPwleSizeMax,
96                 baseVibratorInfo.mQFactor, baseVibratorInfo.mFrequencyProfileLegacy,
97                 baseVibratorInfo.mFrequencyProfile, baseVibratorInfo.mMaxEnvelopeEffectSize,
98                 baseVibratorInfo.mMinEnvelopeEffectControlPointDurationMillis,
99                 baseVibratorInfo.mMaxEnvelopeEffectControlPointDurationMillis);
100     }
101 
102     /**
103      * Default constructor.
104      *
105      * @param id                       The vibrator id.
106      * @param capabilities             All capability flags of the vibrator, defined in
107      *                                 IVibrator.CAP_*.
108      * @param supportedEffects         All supported predefined effects, enum values from
109      *                                 {@link android.hardware.vibrator.Effect}.
110      * @param supportedBraking         All supported braking types, enum values from {@link
111      *                                 Braking}.
112      * @param supportedPrimitives      All supported primitive effects, key are enum values from
113      *                                 {@link android.hardware.vibrator.CompositePrimitive} and
114      *                                 values are estimated durations in milliseconds.
115      * @param primitiveDelayMax        The maximum delay that can be set to a composition primitive
116      *                                 in milliseconds.
117      * @param compositionSizeMax       The maximum number of primitives supported by a composition.
118      * @param pwlePrimitiveDurationMax The maximum duration of a PWLE primitive in milliseconds.
119      * @param pwleSizeMax              The maximum number of primitives supported by a PWLE
120      *                                 composition.
121      * @param qFactor                  The vibrator quality factor.
122      * @param frequencyProfileLegacy   The description of the vibrator supported frequencies and max
123      *                                 amplitude mappings.
124      * @param frequencyProfile         The description of the vibrator supported frequencies and
125      *                                 output acceleration mappings.
126      * @param maxEnvelopeEffectSize    The maximum number of control points supported for an
127      *                                 envelope effect.
128      * @param minEnvelopeEffectControlPointDurationMillis   The minimum duration supported
129      *                                                      between two control points within an
130      *                                                      envelope effect.
131      * @param maxEnvelopeEffectControlPointDurationMillis   The maximum duration supported
132      *                                                      between two control points within an
133      *                                                      envelope effect.
134      *
135      * @hide
136      */
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 FrequencyProfileLegacy frequencyProfileLegacy, @NonNull FrequencyProfile frequencyProfile, int maxEnvelopeEffectSize, int minEnvelopeEffectControlPointDurationMillis, int maxEnvelopeEffectControlPointDurationMillis)137     public VibratorInfo(int id, long capabilities, @Nullable SparseBooleanArray supportedEffects,
138             @Nullable SparseBooleanArray supportedBraking,
139             @NonNull SparseIntArray supportedPrimitives, int primitiveDelayMax,
140             int compositionSizeMax, int pwlePrimitiveDurationMax, int pwleSizeMax,
141             float qFactor, @NonNull FrequencyProfileLegacy frequencyProfileLegacy,
142             @NonNull FrequencyProfile frequencyProfile, int maxEnvelopeEffectSize,
143             int minEnvelopeEffectControlPointDurationMillis,
144             int maxEnvelopeEffectControlPointDurationMillis) {
145         Preconditions.checkNotNull(supportedPrimitives);
146         Preconditions.checkNotNull(frequencyProfileLegacy);
147         Preconditions.checkNotNull(frequencyProfile);
148         mId = id;
149         mCapabilities = capabilities;
150         mSupportedEffects = supportedEffects == null ? null : supportedEffects.clone();
151         mSupportedBraking = supportedBraking == null ? null : supportedBraking.clone();
152         mSupportedPrimitives = supportedPrimitives.clone();
153         mPrimitiveDelayMax = primitiveDelayMax;
154         mCompositionSizeMax = compositionSizeMax;
155         mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
156         mPwleSizeMax = pwleSizeMax;
157         mQFactor = qFactor;
158         mFrequencyProfileLegacy = frequencyProfileLegacy;
159         mFrequencyProfile = frequencyProfile;
160         mMaxEnvelopeEffectSize = maxEnvelopeEffectSize;
161         mMinEnvelopeEffectControlPointDurationMillis =
162                 minEnvelopeEffectControlPointDurationMillis;
163         mMaxEnvelopeEffectControlPointDurationMillis =
164                 maxEnvelopeEffectControlPointDurationMillis;
165     }
166 
167     @Override
writeToParcel(Parcel dest, int flags)168     public void writeToParcel(Parcel dest, int flags) {
169         dest.writeInt(mId);
170         dest.writeLong(mCapabilities);
171         dest.writeSparseBooleanArray(mSupportedEffects);
172         dest.writeSparseBooleanArray(mSupportedBraking);
173         dest.writeSparseIntArray(mSupportedPrimitives);
174         dest.writeInt(mPrimitiveDelayMax);
175         dest.writeInt(mCompositionSizeMax);
176         dest.writeInt(mPwlePrimitiveDurationMax);
177         dest.writeInt(mPwleSizeMax);
178         dest.writeFloat(mQFactor);
179         mFrequencyProfileLegacy.writeToParcel(dest, flags);
180         mFrequencyProfile.writeToParcel(dest, flags);
181         dest.writeInt(mMaxEnvelopeEffectSize);
182         dest.writeInt(mMinEnvelopeEffectControlPointDurationMillis);
183         dest.writeInt(mMaxEnvelopeEffectControlPointDurationMillis);
184     }
185 
186     @Override
describeContents()187     public int describeContents() {
188         return 0;
189     }
190 
191     @Override
equals(Object o)192     public boolean equals(Object o) {
193         if (this == o) {
194             return true;
195         }
196         if (!(o instanceof VibratorInfo)) {
197             return false;
198         }
199         VibratorInfo that = (VibratorInfo) o;
200         return mId == that.mId && equalContent(that);
201     }
202 
203     /**
204      * Returns {@code true} only if the properties and capabilities of the provided info, except for
205      * the ID, equals to this info. Returns {@code false} otherwise.
206      *
207      * @hide
208      */
equalContent(VibratorInfo that)209     public boolean equalContent(VibratorInfo that) {
210         int supportedPrimitivesCount = mSupportedPrimitives.size();
211         if (supportedPrimitivesCount != that.mSupportedPrimitives.size()) {
212             return false;
213         }
214         for (int i = 0; i < supportedPrimitivesCount; i++) {
215             if (mSupportedPrimitives.keyAt(i) != that.mSupportedPrimitives.keyAt(i)) {
216                 return false;
217             }
218             if (mSupportedPrimitives.valueAt(i) != that.mSupportedPrimitives.valueAt(i)) {
219                 return false;
220             }
221         }
222         return mCapabilities == that.mCapabilities
223                 && mPrimitiveDelayMax == that.mPrimitiveDelayMax
224                 && mCompositionSizeMax == that.mCompositionSizeMax
225                 && mPwlePrimitiveDurationMax == that.mPwlePrimitiveDurationMax
226                 && mPwleSizeMax == that.mPwleSizeMax
227                 && Objects.equals(mSupportedEffects, that.mSupportedEffects)
228                 && Objects.equals(mSupportedBraking, that.mSupportedBraking)
229                 && Objects.equals(mQFactor, that.mQFactor)
230                 && Objects.equals(mFrequencyProfileLegacy, that.mFrequencyProfileLegacy)
231                 && Objects.equals(mFrequencyProfile, that.mFrequencyProfile)
232                 && mMaxEnvelopeEffectSize == that.mMaxEnvelopeEffectSize
233                 && mMinEnvelopeEffectControlPointDurationMillis
234                 == that.mMinEnvelopeEffectControlPointDurationMillis
235                 && mMaxEnvelopeEffectControlPointDurationMillis
236                 == that.mMaxEnvelopeEffectControlPointDurationMillis;
237     }
238 
239     @Override
hashCode()240     public int hashCode() {
241         int hashCode = Objects.hash(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
242                 mQFactor, mFrequencyProfileLegacy, mFrequencyProfile);
243         for (int i = 0; i < mSupportedPrimitives.size(); i++) {
244             hashCode = 31 * hashCode + mSupportedPrimitives.keyAt(i);
245             hashCode = 31 * hashCode + mSupportedPrimitives.valueAt(i);
246         }
247         return hashCode;
248     }
249 
250     @Override
toString()251     public String toString() {
252         return "VibratorInfo{"
253                 + "mId=" + mId
254                 + ", mCapabilities=" + Arrays.toString(getCapabilitiesNames())
255                 + ", mCapabilities flags=" + Long.toBinaryString(mCapabilities)
256                 + ", mSupportedEffects=" + Arrays.toString(getSupportedEffectsNames())
257                 + ", mSupportedBraking=" + Arrays.toString(getSupportedBrakingNames())
258                 + ", mSupportedPrimitives=" + Arrays.toString(getSupportedPrimitivesNames())
259                 + ", mPrimitiveDelayMax=" + mPrimitiveDelayMax
260                 + ", mCompositionSizeMax=" + mCompositionSizeMax
261                 + ", mPwlePrimitiveDurationMax=" + mPwlePrimitiveDurationMax
262                 + ", mPwleSizeMax=" + mPwleSizeMax
263                 + ", mQFactor=" + mQFactor
264                 + ", mFrequencyProfileLegacy=" + mFrequencyProfileLegacy
265                 + ", mFrequencyProfile=" + mFrequencyProfile
266                 + ", mMaxEnvelopeEffectSize=" + mMaxEnvelopeEffectSize
267                 + ", mMinEnvelopeEffectControlPointDurationMillis="
268                 + mMinEnvelopeEffectControlPointDurationMillis
269                 + ", mMaxEnvelopeEffectControlPointDurationMillis="
270                 + mMaxEnvelopeEffectControlPointDurationMillis
271                 + '}';
272     }
273 
274     /** @hide */
dump(IndentingPrintWriter pw)275     public void dump(IndentingPrintWriter pw) {
276         pw.println("VibratorInfo:");
277         pw.increaseIndent();
278         pw.println("id = " + mId);
279         pw.println("capabilities = " + Arrays.toString(getCapabilitiesNames()));
280         pw.println("capabilitiesFlags = " + Long.toBinaryString(mCapabilities));
281         pw.println("supportedEffects = " + Arrays.toString(getSupportedEffectsNames()));
282         pw.println("supportedPrimitives = " + Arrays.toString(getSupportedPrimitivesNames()));
283         pw.println("supportedBraking = " + Arrays.toString(getSupportedBrakingNames()));
284         pw.println("primitiveDelayMax = " + mPrimitiveDelayMax);
285         pw.println("compositionSizeMax = " + mCompositionSizeMax);
286         pw.println("pwlePrimitiveDurationMax = " + mPwlePrimitiveDurationMax);
287         pw.println("pwleSizeMax = " + mPwleSizeMax);
288         pw.println("q-factor = " + mQFactor);
289         pw.println("frequencyProfileLegacy = " + mFrequencyProfileLegacy);
290         pw.println("frequencyProfile = " + mFrequencyProfile);
291         pw.println("mMaxEnvelopeEffectSize = " + mMaxEnvelopeEffectSize);
292         pw.println("mMinEnvelopeEffectControlPointDurationMillis = "
293                 + mMinEnvelopeEffectControlPointDurationMillis);
294         pw.println("mMaxEnvelopeEffectControlPointDurationMillis = "
295                 + mMaxEnvelopeEffectControlPointDurationMillis);
296         pw.decreaseIndent();
297     }
298 
299     /** Return the id of this vibrator. */
getId()300     public int getId() {
301         return mId;
302     }
303 
304     /**
305      * Check whether the vibrator has amplitude control.
306      *
307      * @return True if the hardware can control the amplitude of the vibrations, otherwise false.
308      */
hasAmplitudeControl()309     public boolean hasAmplitudeControl() {
310         return hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL);
311     }
312 
313     /**
314      * Check whether the vibrator has frequency control.
315      *
316      * @return True if the hardware can control the frequency of the vibrations, otherwise false.
317      */
hasFrequencyControl()318     public boolean hasFrequencyControl() {
319         return hasCapability(IVibrator.CAP_FREQUENCY_CONTROL);
320     }
321 
322     /**
323      * Returns a default value to be applied to composed PWLE effects for braking.
324      *
325      * @return a supported braking value, one of android.hardware.vibrator.Braking.*
326      * @hide
327      */
getDefaultBraking()328     public int getDefaultBraking() {
329         if (mSupportedBraking != null) {
330             int size = mSupportedBraking.size();
331             for (int i = 0; i < size; i++) {
332                 if (mSupportedBraking.keyAt(i) != Braking.NONE) {
333                     return mSupportedBraking.keyAt(i);
334                 }
335             }
336         }
337         return Braking.NONE;
338     }
339 
340     /** @hide */
341     @Nullable
getSupportedBraking()342     public SparseBooleanArray getSupportedBraking() {
343         if (mSupportedBraking == null) {
344             return null;
345         }
346         return mSupportedBraking.clone();
347     }
348 
349     /** @hide */
isBrakingSupportKnown()350     public boolean isBrakingSupportKnown() {
351         return mSupportedBraking != null;
352     }
353 
354     /** @hide */
hasBrakingSupport(@raking int braking)355     public boolean hasBrakingSupport(@Braking int braking) {
356         return (mSupportedBraking != null) && mSupportedBraking.get(braking);
357     }
358 
359     /** @hide */
isEffectSupportKnown()360     public boolean isEffectSupportKnown() {
361         return mSupportedEffects != null;
362     }
363 
364     /**
365      * Query whether the vibrator supports the given effect.
366      *
367      * @param effectId Which effects to query for.
368      * @return {@link Vibrator#VIBRATION_EFFECT_SUPPORT_YES} if the effect is supported,
369      * {@link Vibrator#VIBRATION_EFFECT_SUPPORT_NO} if it isn't supported, or
370      * {@link Vibrator#VIBRATION_EFFECT_SUPPORT_UNKNOWN} if the system can't determine whether it's
371      * supported or not.
372      */
373     @Vibrator.VibrationEffectSupport
isEffectSupported(@ibrationEffect.EffectType int effectId)374     public int isEffectSupported(@VibrationEffect.EffectType int effectId) {
375         if (mSupportedEffects == null) {
376             return Vibrator.VIBRATION_EFFECT_SUPPORT_UNKNOWN;
377         }
378         return mSupportedEffects.get(effectId) ? Vibrator.VIBRATION_EFFECT_SUPPORT_YES
379                 : Vibrator.VIBRATION_EFFECT_SUPPORT_NO;
380     }
381 
382     /** @hide */
383     @Nullable
getSupportedEffects()384     public SparseBooleanArray getSupportedEffects() {
385         if (mSupportedEffects == null) {
386             return null;
387         }
388         return mSupportedEffects.clone();
389     }
390 
391     /**
392      * Query whether the vibrator supports the given primitive.
393      *
394      * @param primitiveId Which primitives to query for.
395      * @return Whether the primitive is supported.
396      */
isPrimitiveSupported( @ibrationEffect.Composition.PrimitiveType int primitiveId)397     public boolean isPrimitiveSupported(
398             @VibrationEffect.Composition.PrimitiveType int primitiveId) {
399         return hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)
400                 && (mSupportedPrimitives.indexOfKey(primitiveId) >= 0);
401     }
402 
403     /**
404      * Query whether or not the vibrator supports all components of a given {@link VibrationEffect}
405      * (i.e. the vibrator can play the given effect as intended).
406      *
407      * <p>See {@link Vibrator#areVibrationFeaturesSupported(VibrationEffect)} for more
408      * information on how the vibrator support is determined.
409      *
410      * @param effect the {@link VibrationEffect} to check if it is supported
411      * @return {@code true} if the vibrator can play the given {@code effect} as intended,
412      *         {@code false} otherwise.
413      *
414      * @hide
415      */
areVibrationFeaturesSupported(@onNull VibrationEffect effect)416     public boolean areVibrationFeaturesSupported(@NonNull VibrationEffect effect) {
417         return effect.areVibrationFeaturesSupported(this);
418     }
419 
420     /**
421      * Query the estimated duration of given primitive.
422      *
423      * @param primitiveId Which primitives to query for.
424      * @return The duration in milliseconds estimated for the primitive, or zero if primitive not
425      * supported.
426      */
getPrimitiveDuration( @ibrationEffect.Composition.PrimitiveType int primitiveId)427     public int getPrimitiveDuration(
428             @VibrationEffect.Composition.PrimitiveType int primitiveId) {
429         return mSupportedPrimitives.get(primitiveId);
430     }
431 
432     /** @hide */
getSupportedPrimitives()433     public SparseIntArray getSupportedPrimitives() {
434         return mSupportedPrimitives.clone();
435     }
436 
437     /**
438      * Query the maximum delay supported for a primitive in a composed effect.
439      *
440      * @return The max delay in milliseconds, or zero if unlimited.
441      */
getPrimitiveDelayMax()442     public int getPrimitiveDelayMax() {
443         return mPrimitiveDelayMax;
444     }
445 
446     /**
447      * Query the maximum number of primitives supported in a composed effect.
448      *
449      * @return The max number of primitives supported, or zero if unlimited.
450      */
getCompositionSizeMax()451     public int getCompositionSizeMax() {
452         return mCompositionSizeMax;
453     }
454 
455     /**
456      * Query the maximum duration supported for a primitive in a PWLE composition.
457      *
458      * @return The max duration in milliseconds, or zero if unlimited.
459      */
getPwlePrimitiveDurationMax()460     public int getPwlePrimitiveDurationMax() {
461         return mPwlePrimitiveDurationMax;
462     }
463 
464     /**
465      * Query the maximum number of primitives supported in a PWLE composition.
466      *
467      * @return The max number of primitives supported, or zero if unlimited.
468      */
getPwleSizeMax()469     public int getPwleSizeMax() {
470         return mPwleSizeMax;
471     }
472 
473     /**
474      * Check whether the vibrator supports the creation of envelope effects.
475      *
476      * <p>See {@link Vibrator#areEnvelopeEffectsSupported()} for more information on envelope
477      * effects.
478      *
479      * @return True if the hardware supports creating envelope effects, false otherwise.
480      */
areEnvelopeEffectsSupported()481     public boolean areEnvelopeEffectsSupported() {
482         return hasCapability(
483                 IVibrator.CAP_FREQUENCY_CONTROL | IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2);
484     }
485 
486     /**
487      * Calculates the maximum allowed duration for an envelope effect, measured in milliseconds.
488      *
489      * @return The maximum duration (in milliseconds) that an envelope effect can have.
490      */
getMaxEnvelopeEffectDurationMillis()491     public int getMaxEnvelopeEffectDurationMillis() {
492         return mMaxEnvelopeEffectSize * mMaxEnvelopeEffectControlPointDurationMillis;
493     }
494 
495     /**
496      * Gets the maximum number of control points supported for envelope effects on this device.
497      *
498      * @return The maximum number of control points that can be used to define an envelope effect.
499      */
getMaxEnvelopeEffectSize()500     public int getMaxEnvelopeEffectSize() {
501         return mMaxEnvelopeEffectSize;
502     }
503 
504     /**
505      * Gets the minimum allowed duration for any individual segment within an envelope effect,
506      * measured in milliseconds.
507      *
508      * @return The minimum duration (in milliseconds) that a segment within an envelope effect
509      * can have.
510      */
getMinEnvelopeEffectControlPointDurationMillis()511     public int getMinEnvelopeEffectControlPointDurationMillis() {
512         return mMinEnvelopeEffectControlPointDurationMillis;
513     }
514 
515     /**
516      * Gets the maximum allowed duration for any individual segment within an envelope effect,
517      * measured in milliseconds.
518      *
519      * @return The maximum duration (in milliseconds) that a segment within an envelope effect
520      * can have.
521      */
getMaxEnvelopeEffectControlPointDurationMillis()522     public int getMaxEnvelopeEffectControlPointDurationMillis() {
523         return mMaxEnvelopeEffectControlPointDurationMillis;
524     }
525 
526     /**
527      * Check against this vibrator capabilities.
528      *
529      * @param capability one of IVibrator.CAP_*
530      * @return true if this vibrator has this capability, false otherwise
531      * @hide
532      */
hasCapability(long capability)533     public boolean hasCapability(long capability) {
534         return (mCapabilities & capability) == capability;
535     }
536 
537     /**
538      * Gets the resonant frequency of the vibrator.
539      *
540      * @return the resonant frequency of the vibrator, or {@link Float#NaN NaN} if it's unknown or
541      * this vibrator is a composite of multiple physical devices.
542      */
getResonantFrequencyHz()543     public float getResonantFrequencyHz() {
544         if (Flags.normalizedPwleEffects()) {
545             return mFrequencyProfile.mResonantFrequencyHz;
546         }
547         return mFrequencyProfileLegacy.mResonantFrequencyHz;
548     }
549 
550     /**
551      * Gets the <a href="https://en.wikipedia.org/wiki/Q_factor">Q factor</a> of the vibrator.
552      *
553      * @return the Q factor of the vibrator, or {@link Float#NaN NaN} if it's unknown or
554      * this vibrator is a composite of multiple physical devices.
555      */
getQFactor()556     public float getQFactor() {
557         return mQFactor;
558     }
559 
560     /**
561      * Gets the profile of supported frequencies, including the measurements of maximum relative
562      * output acceleration for supported vibration frequencies.
563      *
564      * <p>If the devices does not have frequency control then the profile should be empty.
565      */
566     @NonNull
getFrequencyProfileLegacy()567     public FrequencyProfileLegacy getFrequencyProfileLegacy() {
568         return mFrequencyProfileLegacy;
569     }
570 
571     /**
572      * Gets the profile of supported frequencies, including the measurements of maximum
573      * output acceleration for supported vibration frequencies.
574      *
575      * <p>If the devices does not have frequency control then the profile should be empty.
576      */
577     @NonNull
getFrequencyProfile()578     public FrequencyProfile getFrequencyProfile() {
579         return mFrequencyProfile;
580     }
581 
582     /** Returns a single int representing all the capabilities of the vibrator. */
getCapabilities()583     public long getCapabilities() {
584         return mCapabilities;
585     }
586 
getCapabilitiesNames()587     private String[] getCapabilitiesNames() {
588         List<String> names = new ArrayList<>();
589         if (hasCapability(IVibrator.CAP_ON_CALLBACK)) {
590             names.add("ON_CALLBACK");
591         }
592         if (hasCapability(IVibrator.CAP_PERFORM_CALLBACK)) {
593             names.add("PERFORM_CALLBACK");
594         }
595         if (hasCapability(IVibrator.CAP_COMPOSE_EFFECTS)) {
596             names.add("COMPOSE_EFFECTS");
597         }
598         if (hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS)) {
599             names.add("COMPOSE_PWLE_EFFECTS");
600         }
601         if (hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) {
602             names.add("ALWAYS_ON_CONTROL");
603         }
604         if (hasCapability(IVibrator.CAP_AMPLITUDE_CONTROL)) {
605             names.add("AMPLITUDE_CONTROL");
606         }
607         if (hasCapability(IVibrator.CAP_FREQUENCY_CONTROL)) {
608             names.add("FREQUENCY_CONTROL");
609         }
610         if (hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) {
611             names.add("EXTERNAL_CONTROL");
612         }
613         if (hasCapability(IVibrator.CAP_EXTERNAL_AMPLITUDE_CONTROL)) {
614             names.add("EXTERNAL_AMPLITUDE_CONTROL");
615         }
616         if (hasCapability(IVibrator.CAP_COMPOSE_PWLE_EFFECTS_V2)) {
617             names.add("CAP_COMPOSE_PWLE_EFFECTS_V2");
618         }
619         return names.toArray(new String[names.size()]);
620     }
621 
getSupportedEffectsNames()622     private String[] getSupportedEffectsNames() {
623         if (mSupportedEffects == null) {
624             return new String[0];
625         }
626         String[] names = new String[mSupportedEffects.size()];
627         for (int i = 0; i < mSupportedEffects.size(); i++) {
628             names[i] = VibrationEffect.effectIdToString(mSupportedEffects.keyAt(i));
629         }
630         return names;
631     }
632 
getSupportedBrakingNames()633     private String[] getSupportedBrakingNames() {
634         if (mSupportedBraking == null) {
635             return new String[0];
636         }
637         String[] names = new String[mSupportedBraking.size()];
638         for (int i = 0; i < mSupportedBraking.size(); i++) {
639             switch (mSupportedBraking.keyAt(i)) {
640                 case Braking.NONE:
641                     names[i] = "NONE";
642                     break;
643                 case Braking.CLAB:
644                     names[i] = "CLAB";
645                     break;
646                 default:
647                     names[i] = Integer.toString(mSupportedBraking.keyAt(i));
648             }
649         }
650         return names;
651     }
652 
getSupportedPrimitivesNames()653     private String[] getSupportedPrimitivesNames() {
654         int supportedPrimitivesCount = mSupportedPrimitives.size();
655         String[] names = new String[supportedPrimitivesCount];
656         for (int i = 0; i < supportedPrimitivesCount; i++) {
657             names[i] = VibrationEffect.Composition.primitiveToString(mSupportedPrimitives.keyAt(i))
658                     + "(" + mSupportedPrimitives.valueAt(i) + "ms)";
659         }
660         return names;
661     }
662 
663     /**
664      * Describes the maximum output acceleration that can be achieved for each supported
665      * frequency in a specific vibrator.
666      *
667      * @hide
668      */
669     public static final class FrequencyProfile implements Parcelable {
670 
671         private final float[] mFrequenciesHz;
672         private final float[] mOutputAccelerationsGs;
673         private final float mResonantFrequencyHz;
674         private final float mMaxOutputAccelerationGs;
675         private final float mMinFrequencyHz;
676         private final float mMaxFrequencyHz;
677 
FrequencyProfile(Parcel in)678         public FrequencyProfile(Parcel in) {
679             this(in.readFloat(), in.createFloatArray(), in.createFloatArray());
680         }
681 
682         /**
683          * Default constructor.
684          *
685          * @param resonantFrequencyHz   The vibrator resonant frequency, in hertz.
686          * @param frequenciesHz         The supported vibration frequencies, in hertz.
687          * @param outputAccelerationsGs The maximum achievable output acceleration (in Gs) the
688          *                              device can reach at the supported frequencies.
689          */
FrequencyProfile(float resonantFrequencyHz, float[] frequenciesHz, float[] outputAccelerationsGs)690         public FrequencyProfile(float resonantFrequencyHz, float[] frequenciesHz,
691                 float[] outputAccelerationsGs) {
692 
693             mResonantFrequencyHz = resonantFrequencyHz;
694 
695             boolean isValid = !Float.isNaN(resonantFrequencyHz)
696                     && (resonantFrequencyHz > 0)
697                     && (frequenciesHz != null && outputAccelerationsGs != null)
698                     && (frequenciesHz.length == outputAccelerationsGs.length)
699                     && (frequenciesHz.length > 0);
700 
701             if (!isValid) {
702                 mFrequenciesHz = null;
703                 mOutputAccelerationsGs = null;
704                 mMinFrequencyHz = Float.NaN;
705                 mMaxFrequencyHz = Float.NaN;
706                 mMaxOutputAccelerationGs = Float.NaN;
707                 return;
708             }
709 
710             TreeMap<Float, Float> frequencyToOutputAccelerationMap = new TreeMap<>();
711 
712             for (int i = 0; i < frequenciesHz.length; i++) {
713                 frequencyToOutputAccelerationMap.putIfAbsent(frequenciesHz[i],
714                         outputAccelerationsGs[i]);
715             }
716 
717             float[] frequencies = new float[frequencyToOutputAccelerationMap.size()];
718             float[] accelerations = new float[frequencyToOutputAccelerationMap.size()];
719             float maxOutputAccelerationGs = 0;
720             int i = 0;
721             for (Map.Entry<Float, Float> entry : frequencyToOutputAccelerationMap.entrySet()) {
722                 frequencies[i] = entry.getKey();
723                 accelerations[i] = entry.getValue();
724                 maxOutputAccelerationGs = Math.max(maxOutputAccelerationGs, entry.getValue());
725                 i++;
726             }
727 
728             mFrequenciesHz = frequencies;
729             mOutputAccelerationsGs = accelerations;
730             mMinFrequencyHz = mFrequenciesHz[0];
731             mMaxFrequencyHz = mFrequenciesHz[mFrequenciesHz.length - 1];
732             mMaxOutputAccelerationGs = maxOutputAccelerationGs;
733         }
734 
735         /** Returns true if the supported frequency range is null. */
isEmpty()736         public boolean isEmpty() {
737             return mFrequenciesHz == null;
738         }
739 
740         /**
741          * Returns a list of available frequencies.
742          */
743         @Nullable
getFrequenciesHz()744         public float[] getFrequenciesHz() {
745             return mFrequenciesHz;
746         }
747 
748         /** Returns the list of available output accelerations */
749         @Nullable
getOutputAccelerationsGs()750         public float[] getOutputAccelerationsGs() {
751             return mOutputAccelerationsGs;
752         }
753 
754         /** Maximum output acceleration reachable in Gs when amplitude is 1.0f. */
getMaxOutputAccelerationGs()755         public float getMaxOutputAccelerationGs() {
756             return mMaxOutputAccelerationGs;
757         }
758 
759         /**
760          * Calculates the maximum output acceleration for a given frequency using linear
761          * interpolation.
762          *
763          * @param frequencyHz frequency, in hertz, for query.
764          * @return the maximum output acceleration for the given frequency.
765          */
getOutputAccelerationGs(float frequencyHz)766         public float getOutputAccelerationGs(float frequencyHz) {
767             if (mFrequenciesHz == null) {
768                 return Float.NaN;
769             }
770 
771             if (frequencyHz < mMinFrequencyHz || frequencyHz > mMaxFrequencyHz) {
772                 // Outside supported frequency range, not able to vibrate at this frequency.
773                 return 0;
774             }
775 
776             int idx = Arrays.binarySearch(mFrequenciesHz, frequencyHz);
777             if (idx >= 0) {
778                 return mOutputAccelerationsGs[idx];
779             }
780 
781             // This indicates that the value was not found in the list. Adjust index of the
782             // insertion point to be at the lower bound.
783             idx = -idx - 2;
784 
785             // Linearly interpolate the output acceleration based on the frequency.
786             return MathUtils.constrainedMap(
787                     mOutputAccelerationsGs[idx], mOutputAccelerationsGs[idx + 1],
788                     mFrequenciesHz[idx], mFrequenciesHz[idx + 1],
789                     frequencyHz);
790         }
791 
792         /** The minimum frequency supported, in hertz. */
getMinFrequencyHz()793         public float getMinFrequencyHz() {
794             return mMinFrequencyHz;
795         }
796 
797         /** The maximum frequency supported, in hertz. */
getMaxFrequencyHz()798         public float getMaxFrequencyHz() {
799             return mMaxFrequencyHz;
800         }
801 
802         /**
803          * Returns the frequency range that supports the specified minimum output
804          * acceleration.
805          *
806          * @return The frequency range, or null if the specified acceleration
807          *         is not achievable on the device.
808          */
809         @Nullable
getFrequencyRangeHz(float minOutputAcceleration)810         public Range<Float> getFrequencyRangeHz(float minOutputAcceleration) {
811             if (mFrequenciesHz == null || mOutputAccelerationsGs == null
812                     || minOutputAcceleration > mMaxOutputAccelerationGs) {
813                 return null; // No frequency range available
814             }
815 
816             if (minOutputAcceleration <= 0) {
817                 return new Range<>(mMinFrequencyHz, mMaxFrequencyHz);
818             }
819 
820             float minFrequency = Float.NaN;
821             float maxFrequency = Float.NaN;
822             int lowerFrequencyBoundIndex = 0;
823             // Find the lower frequency bound
824             for (int i = 0; i < mOutputAccelerationsGs.length; i++) {
825                 if (mOutputAccelerationsGs[i] >= minOutputAcceleration) {
826                     if (i == 0) {
827                         minFrequency = mMinFrequencyHz;
828                     } else {
829                         minFrequency = MathUtils.constrainedMap(
830                                 mFrequenciesHz[i - 1], mFrequenciesHz[i],
831                                 mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i],
832                                 minOutputAcceleration);
833                     }
834                     lowerFrequencyBoundIndex = i;
835                     break; // Found the lower bound
836                 }
837             }
838 
839             if (Float.isNaN(minFrequency)) {
840                 // Lower bound was not found
841                 return null;
842             }
843 
844             // Find the upper frequency bound
845             for (int i = lowerFrequencyBoundIndex; i < mOutputAccelerationsGs.length; i++) {
846                 if (mOutputAccelerationsGs[i] <= minOutputAcceleration) {
847                     maxFrequency = MathUtils.constrainedMap(
848                             mFrequenciesHz[i - 1], mFrequenciesHz[i],
849                             mOutputAccelerationsGs[i - 1], mOutputAccelerationsGs[i],
850                             minOutputAcceleration);
851                     break; // Found the upper bound
852                 }
853             }
854 
855             if (Float.isNaN(maxFrequency)) {
856                 // If the upper bound was not found, the specified output acceleration is
857                 // achievable at all remaining frequencies.
858                 maxFrequency = mMaxFrequencyHz;
859             }
860 
861             return new Range<>(minFrequency, maxFrequency);
862         }
863 
864         @Override
writeToParcel(Parcel dest, int flags)865         public void writeToParcel(Parcel dest, int flags) {
866             dest.writeFloat(mResonantFrequencyHz);
867             dest.writeFloatArray(mFrequenciesHz);
868             dest.writeFloatArray(mOutputAccelerationsGs);
869         }
870 
871         @Override
describeContents()872         public int describeContents() {
873             return 0;
874         }
875 
876         @Override
equals(Object o)877         public boolean equals(Object o) {
878             if (this == o) {
879                 return true;
880             }
881             if (!(o instanceof FrequencyProfile that)) {
882                 return false;
883             }
884             return Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
885                     && Arrays.equals(mFrequenciesHz, that.mFrequenciesHz)
886                     && Arrays.equals(mOutputAccelerationsGs, that.mOutputAccelerationsGs);
887         }
888 
889         @Override
hashCode()890         public int hashCode() {
891             return Objects.hash(mResonantFrequencyHz, Arrays.hashCode(mFrequenciesHz),
892                     Arrays.hashCode(mOutputAccelerationsGs));
893         }
894 
895         @Override
toString()896         public String toString() {
897             return "FrequencyProfile{"
898                     + "mResonantFrequency=" + mResonantFrequencyHz
899                     + ", mFrequenciesHz=" + Arrays.toString(mFrequenciesHz)
900                     + ", mOutputAccelerationsGs=" + Arrays.toString(mOutputAccelerationsGs)
901                     + ", mMinFrequencyHz=" + mMinFrequencyHz
902                     + ", mMaxFrequencyHz=" + mMaxFrequencyHz
903                     + ", mMaxOutputAccelerationGs=" + mMaxOutputAccelerationGs
904                     + '}';
905         }
906 
907         @NonNull
908         public static final Creator<FrequencyProfile> CREATOR =
909                 new Creator<FrequencyProfile>() {
910                     @Override
911                     public FrequencyProfile createFromParcel(Parcel in) {
912                         return new FrequencyProfile(in);
913                     }
914 
915                     @Override
916                     public FrequencyProfile[] newArray(int size) {
917                         return new FrequencyProfile[size];
918                     }
919                 };
920 
deduplicateAndSortList(List<Pair<Float, Float>> list)921         private static void deduplicateAndSortList(List<Pair<Float, Float>> list) {
922             if (list == null || list.size() < 2) {
923                 return; // Nothing to dedupe
924             }
925 
926             list.sort(Comparator.comparing(pair -> pair.first));
927 
928             // Remove duplicates from the list
929             int writeIndex = 1;
930             for (int i = 1; i < list.size(); i++) {
931                 Pair<Float, Float> currentPair = list.get(i);
932                 Pair<Float, Float> previousPair = list.get(writeIndex - 1);
933 
934                 if (currentPair.first.compareTo(previousPair.first) != 0) {
935                     list.set(writeIndex++, currentPair);
936                 }
937             }
938             list.subList(writeIndex, list.size()).clear();
939         }
940 
extractFrequencyToOutputAccelerationData( float[] frequencies, float[] outputAccelerations)941         private static ArrayList<Pair<Float, Float>> extractFrequencyToOutputAccelerationData(
942                 float[] frequencies, float[] outputAccelerations) {
943 
944             if (frequencies == null || outputAccelerations == null
945                     || frequencies.length == 0
946                     || frequencies.length != outputAccelerations.length) {
947                 return new ArrayList<>(); // Return empty list for invalid or mismatched data
948             }
949 
950             ArrayList<Pair<Float, Float>> frequencyToOutputAccelerationList = new ArrayList<>(
951                     frequencies.length);
952             for (int i = 0; i < frequencies.length; i++) {
953                 frequencyToOutputAccelerationList.add(
954                         new Pair<>(frequencies[i], outputAccelerations[i]));
955             }
956 
957             return frequencyToOutputAccelerationList;
958         }
959     }
960 
961     /**
962      * Describes the maximum relative output acceleration that can be achieved for each supported
963      * frequency in a specific vibrator.
964      *
965      * <p>This profile is defined by the following parameters:
966      *
967      * <ol>
968      *     <li>{@code minFrequencyHz}, {@code resonantFrequencyHz} and {@code frequencyResolutionHz}
969      *         provided by the vibrator in hertz.
970      *     <li>{@code maxAmplitudes} a list of values in [0,1] provided by the vibrator, where
971      *         {@code maxAmplitudes[i]} represents max supported amplitude at frequency
972      *         {@code minFrequencyHz + frequencyResolutionHz * i}.
973      *     <li>{@code maxFrequencyHz = minFrequencyHz
974      *                                     + frequencyResolutionHz * (maxAmplitudes.length-1)}
975      * </ol>
976      *
977      * @hide
978      */
979     public static final class FrequencyProfileLegacy implements Parcelable {
980         @Nullable
981         private final Range<Float> mFrequencyRangeHz;
982         private final float mMinFrequencyHz;
983         private final float mResonantFrequencyHz;
984         private final float mFrequencyResolutionHz;
985         private final float[] mMaxAmplitudes;
986 
FrequencyProfileLegacy(Parcel in)987         FrequencyProfileLegacy(Parcel in) {
988             this(in.readFloat(), in.readFloat(), in.readFloat(), in.createFloatArray());
989         }
990 
991         /**
992          * Default constructor.
993          *
994          * @param resonantFrequencyHz   The vibrator resonant frequency, in hertz.
995          * @param minFrequencyHz        Minimum supported frequency, in hertz.
996          * @param frequencyResolutionHz The frequency resolution, in hertz, used by the max
997          *                              amplitude measurements.
998          * @param maxAmplitudes         The max amplitude supported by each supported frequency,
999          *                              starting at minimum frequency with jumps of frequency
1000          *                              resolution.
1001          * @hide
1002          */
FrequencyProfileLegacy(float resonantFrequencyHz, float minFrequencyHz, float frequencyResolutionHz, float[] maxAmplitudes)1003         public FrequencyProfileLegacy(float resonantFrequencyHz, float minFrequencyHz,
1004                 float frequencyResolutionHz, float[] maxAmplitudes) {
1005             mMinFrequencyHz = minFrequencyHz;
1006             mResonantFrequencyHz = resonantFrequencyHz;
1007             mFrequencyResolutionHz = frequencyResolutionHz;
1008             mMaxAmplitudes = new float[maxAmplitudes == null ? 0 : maxAmplitudes.length];
1009             if (maxAmplitudes != null) {
1010                 System.arraycopy(maxAmplitudes, 0, mMaxAmplitudes, 0, maxAmplitudes.length);
1011             }
1012 
1013             // If any required field is undefined or has a bad value then this profile is invalid.
1014             boolean isValid = !Float.isNaN(resonantFrequencyHz)
1015                     && (resonantFrequencyHz > 0)
1016                     && !Float.isNaN(minFrequencyHz)
1017                     && (minFrequencyHz > 0)
1018                     && !Float.isNaN(frequencyResolutionHz)
1019                     && (frequencyResolutionHz > 0)
1020                     && (mMaxAmplitudes.length > 0);
1021 
1022             // If any max amplitude is outside the allowed range then this profile is invalid.
1023             for (int i = 0; i < mMaxAmplitudes.length; i++) {
1024                 isValid &= (mMaxAmplitudes[i] >= 0) && (mMaxAmplitudes[i] <= 1);
1025             }
1026 
1027             float maxFrequencyHz = isValid
1028                     ? minFrequencyHz + frequencyResolutionHz * (mMaxAmplitudes.length - 1)
1029                     : Float.NaN;
1030 
1031             // If the constraint min < resonant < max is not met then it is invalid.
1032             isValid &= !Float.isNaN(maxFrequencyHz)
1033                     && (resonantFrequencyHz >= minFrequencyHz)
1034                     && (resonantFrequencyHz <= maxFrequencyHz)
1035                     && (minFrequencyHz < maxFrequencyHz);
1036 
1037             mFrequencyRangeHz = isValid ? Range.create(minFrequencyHz, maxFrequencyHz) : null;
1038         }
1039 
1040         /** Returns true if the supported frequency range is empty. */
isEmpty()1041         public boolean isEmpty() {
1042             return mFrequencyRangeHz == null;
1043         }
1044 
1045         /** Returns the supported frequency range, in hertz. */
1046         @Nullable
getFrequencyRangeHz()1047         public Range<Float> getFrequencyRangeHz() {
1048             return mFrequencyRangeHz;
1049         }
1050 
1051         /**
1052          * Returns the maximum relative amplitude the vibrator can reach while playing at the
1053          * given frequency.
1054          *
1055          * @param frequencyHz frequency, in hertz, for query.
1056          * @return A value in [0,1] representing the max relative amplitude supported at the given
1057          * frequency. This will return 0 if the frequency is outside the supported range, or if the
1058          * supported frequency range is empty.
1059          */
getMaxAmplitude(float frequencyHz)1060         public float getMaxAmplitude(float frequencyHz) {
1061             if (isEmpty() || Float.isNaN(frequencyHz) || !mFrequencyRangeHz.contains(frequencyHz)) {
1062                 // Unsupported frequency requested, vibrator cannot play at this frequency.
1063                 return 0;
1064             }
1065 
1066             // Subtract minFrequencyHz to simplify offset calculations.
1067             float mappingFreq = frequencyHz - mMinFrequencyHz;
1068 
1069             // Find the bucket to interpolate within.
1070             // Any calculated index should be safe, except exactly equal to max amplitude can be
1071             // one step too high, so constrain it to guarantee safety.
1072             int startIdx = MathUtils.constrain(
1073                     /* amount= */ (int) Math.floor(mappingFreq / mFrequencyResolutionHz),
1074                     /* low= */ 0, /* high= */ mMaxAmplitudes.length - 1);
1075             int nextIdx = MathUtils.constrain(
1076                     /* amount= */ startIdx + 1,
1077                     /* low= */ 0, /* high= */ mMaxAmplitudes.length - 1);
1078 
1079             // Linearly interpolate the amplitudes based on the frequency range of the bucket.
1080             return MathUtils.constrainedMap(
1081                     mMaxAmplitudes[startIdx], mMaxAmplitudes[nextIdx],
1082                     startIdx * mFrequencyResolutionHz, nextIdx * mFrequencyResolutionHz,
1083                     mappingFreq);
1084         }
1085 
1086         /** Returns the raw list of maximum relative output accelerations from the vibrator. */
1087         @NonNull
getMaxAmplitudes()1088         public float[] getMaxAmplitudes() {
1089             return Arrays.copyOf(mMaxAmplitudes, mMaxAmplitudes.length);
1090         }
1091 
1092         /** Returns the raw frequency resolution used for max amplitude measurements, in hertz. */
getFrequencyResolutionHz()1093         public float getFrequencyResolutionHz() {
1094             return mFrequencyResolutionHz;
1095         }
1096 
1097         @Override
writeToParcel(Parcel dest, int flags)1098         public void writeToParcel(Parcel dest, int flags) {
1099             dest.writeFloat(mResonantFrequencyHz);
1100             dest.writeFloat(mMinFrequencyHz);
1101             dest.writeFloat(mFrequencyResolutionHz);
1102             dest.writeFloatArray(mMaxAmplitudes);
1103         }
1104 
1105         @Override
describeContents()1106         public int describeContents() {
1107             return 0;
1108         }
1109 
1110         @Override
equals(Object o)1111         public boolean equals(Object o) {
1112             if (this == o) {
1113                 return true;
1114             }
1115             if (!(o instanceof FrequencyProfileLegacy)) {
1116                 return false;
1117             }
1118             FrequencyProfileLegacy that = (FrequencyProfileLegacy) o;
1119             return Float.compare(mMinFrequencyHz, that.mMinFrequencyHz) == 0
1120                     && Float.compare(mResonantFrequencyHz, that.mResonantFrequencyHz) == 0
1121                     && Float.compare(mFrequencyResolutionHz, that.mFrequencyResolutionHz) == 0
1122                     && Arrays.equals(mMaxAmplitudes, that.mMaxAmplitudes);
1123         }
1124 
1125         @Override
hashCode()1126         public int hashCode() {
1127             int hashCode = Objects.hash(mMinFrequencyHz, mFrequencyResolutionHz,
1128                     mFrequencyResolutionHz);
1129             hashCode = 31 * hashCode + Arrays.hashCode(mMaxAmplitudes);
1130             return hashCode;
1131         }
1132 
1133         @Override
toString()1134         public String toString() {
1135             return "FrequencyProfileLegacy{"
1136                     + "mFrequencyRange=" + mFrequencyRangeHz
1137                     + ", mMinFrequency=" + mMinFrequencyHz
1138                     + ", mResonantFrequency=" + mResonantFrequencyHz
1139                     + ", mFrequencyResolution=" + mFrequencyResolutionHz
1140                     + ", mMaxAmplitudes count=" + mMaxAmplitudes.length
1141                     + '}';
1142         }
1143 
1144         @NonNull
1145         public static final Creator<FrequencyProfileLegacy> CREATOR =
1146                 new Creator<FrequencyProfileLegacy>() {
1147                     @Override
1148                     public FrequencyProfileLegacy createFromParcel(Parcel in) {
1149                         return new FrequencyProfileLegacy(in);
1150                     }
1151 
1152                     @Override
1153                     public FrequencyProfileLegacy[] newArray(int size) {
1154                         return new FrequencyProfileLegacy[size];
1155                     }
1156                 };
1157     }
1158 
1159     /** @hide */
1160     public static final class Builder {
1161         private final int mId;
1162         private long mCapabilities;
1163         private SparseBooleanArray mSupportedEffects;
1164         private SparseBooleanArray mSupportedBraking;
1165         private SparseIntArray mSupportedPrimitives = new SparseIntArray();
1166         private int mPrimitiveDelayMax;
1167         private int mCompositionSizeMax;
1168         private int mPwlePrimitiveDurationMax;
1169         private int mPwleSizeMax;
1170         private float mQFactor = Float.NaN;
1171         private FrequencyProfileLegacy mFrequencyProfileLegacy =
1172                 new FrequencyProfileLegacy(Float.NaN, Float.NaN, Float.NaN, null);
1173         private FrequencyProfile mFrequencyProfile = new FrequencyProfile(Float.NaN, null,
1174                 null);
1175         private int mMaxEnvelopeEffectSize;
1176         private int mMinEnvelopeEffectControlPointDurationMillis;
1177         private int mMaxEnvelopeEffectControlPointDurationMillis;
1178 
1179         /** A builder class for a {@link VibratorInfo}. */
Builder(int id)1180         public Builder(int id) {
1181             mId = id;
1182         }
1183 
1184         /** Configure the vibrator capabilities with a combination of IVibrator.CAP_* values. */
1185         @NonNull
setCapabilities(long capabilities)1186         public Builder setCapabilities(long capabilities) {
1187             mCapabilities = capabilities;
1188             return this;
1189         }
1190 
1191         /** Configure the effects supported with {@link android.hardware.vibrator.Effect} values. */
1192         @NonNull
setSupportedEffects(int... supportedEffects)1193         public Builder setSupportedEffects(int... supportedEffects) {
1194             mSupportedEffects = toSparseBooleanArray(supportedEffects);
1195             return this;
1196         }
1197 
1198         /** Configure braking supported with {@link android.hardware.vibrator.Braking} values. */
1199         @NonNull
setSupportedBraking(int... supportedBraking)1200         public Builder setSupportedBraking(int... supportedBraking) {
1201             mSupportedBraking = toSparseBooleanArray(supportedBraking);
1202             return this;
1203         }
1204 
1205         /** Configure maximum duration, in milliseconds, of a PWLE primitive. */
1206         @NonNull
setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax)1207         public Builder setPwlePrimitiveDurationMax(int pwlePrimitiveDurationMax) {
1208             mPwlePrimitiveDurationMax = pwlePrimitiveDurationMax;
1209             return this;
1210         }
1211 
1212         /** Configure maximum number of primitives supported in a single PWLE composed effect. */
1213         @NonNull
setPwleSizeMax(int pwleSizeMax)1214         public Builder setPwleSizeMax(int pwleSizeMax) {
1215             mPwleSizeMax = pwleSizeMax;
1216             return this;
1217         }
1218 
1219         /** Configure the duration of a {@link android.hardware.vibrator.CompositePrimitive}. */
1220         @NonNull
setSupportedPrimitive(int primitiveId, int duration)1221         public Builder setSupportedPrimitive(int primitiveId, int duration) {
1222             mSupportedPrimitives.put(primitiveId, duration);
1223             return this;
1224         }
1225 
1226         /** Configure maximum delay, in milliseconds, supported in a composed effect primitive. */
1227         @NonNull
setPrimitiveDelayMax(int primitiveDelayMax)1228         public Builder setPrimitiveDelayMax(int primitiveDelayMax) {
1229             mPrimitiveDelayMax = primitiveDelayMax;
1230             return this;
1231         }
1232 
1233         /** Configure maximum number of primitives supported in a single composed effect. */
1234         @NonNull
setCompositionSizeMax(int compositionSizeMax)1235         public Builder setCompositionSizeMax(int compositionSizeMax) {
1236             mCompositionSizeMax = compositionSizeMax;
1237             return this;
1238         }
1239 
1240         /** Configure the vibrator quality factor. */
1241         @NonNull
setQFactor(float qFactor)1242         public Builder setQFactor(float qFactor) {
1243             mQFactor = qFactor;
1244             return this;
1245         }
1246 
1247         /** Configure the vibrator frequency information like resonant frequency and bandwidth. */
1248         @NonNull
setFrequencyProfileLegacy(@onNull FrequencyProfileLegacy frequencyProfile)1249         public Builder setFrequencyProfileLegacy(@NonNull FrequencyProfileLegacy frequencyProfile) {
1250             mFrequencyProfileLegacy = frequencyProfile;
1251             return this;
1252         }
1253 
1254         /**
1255          * Configure the vibrator frequency information like resonant frequency and frequency to
1256          * output acceleration data.
1257          */
1258         @NonNull
setFrequencyProfile(@onNull FrequencyProfile frequencyProfile)1259         public Builder setFrequencyProfile(@NonNull FrequencyProfile frequencyProfile) {
1260             mFrequencyProfile = frequencyProfile;
1261             return this;
1262         }
1263 
1264         /**
1265          * Configure the maximum number of control points supported for envelope effects on this
1266          * device.
1267          */
1268         @NonNull
setMaxEnvelopeEffectSize(int maxEnvelopeEffectSize)1269         public Builder setMaxEnvelopeEffectSize(int maxEnvelopeEffectSize) {
1270             mMaxEnvelopeEffectSize = maxEnvelopeEffectSize;
1271             return this;
1272         }
1273 
1274         /**
1275          * Configure the minimum supported duration for any individual segment within an
1276          * envelope effect in milliseconds.
1277          */
1278         @NonNull
setMinEnvelopeEffectControlPointDurationMillis( int minEnvelopeEffectControlPointDuration)1279         public Builder setMinEnvelopeEffectControlPointDurationMillis(
1280                 int minEnvelopeEffectControlPointDuration) {
1281             mMinEnvelopeEffectControlPointDurationMillis = minEnvelopeEffectControlPointDuration;
1282             return this;
1283         }
1284 
1285         /**
1286          * Configure the maximum supported duration for any individual segment within an
1287          * envelope effect in milliseconds.
1288          */
1289         @NonNull
setMaxEnvelopeEffectControlPointDurationMillis( int maxEnvelopeEffectControlPointDuration)1290         public Builder setMaxEnvelopeEffectControlPointDurationMillis(
1291                 int maxEnvelopeEffectControlPointDuration) {
1292             mMaxEnvelopeEffectControlPointDurationMillis = maxEnvelopeEffectControlPointDuration;
1293             return this;
1294         }
1295 
1296         /** Build the configured {@link VibratorInfo}. */
1297         @NonNull
build()1298         public VibratorInfo build() {
1299             return new VibratorInfo(mId, mCapabilities, mSupportedEffects, mSupportedBraking,
1300                     mSupportedPrimitives, mPrimitiveDelayMax, mCompositionSizeMax,
1301                     mPwlePrimitiveDurationMax, mPwleSizeMax, mQFactor, mFrequencyProfileLegacy,
1302                     mFrequencyProfile, mMaxEnvelopeEffectSize,
1303                     mMinEnvelopeEffectControlPointDurationMillis,
1304                     mMaxEnvelopeEffectControlPointDurationMillis);
1305         }
1306 
1307         /**
1308          * Create a {@link SparseBooleanArray} from given {@code supportedKeys} where each key is
1309          * mapped
1310          * to {@code true}.
1311          */
1312         @Nullable
toSparseBooleanArray(int[] supportedKeys)1313         private static SparseBooleanArray toSparseBooleanArray(int[] supportedKeys) {
1314             if (supportedKeys == null) {
1315                 return null;
1316             }
1317             SparseBooleanArray array = new SparseBooleanArray();
1318             for (int key : supportedKeys) {
1319                 array.put(key, true);
1320             }
1321             return array;
1322         }
1323     }
1324 
1325     @NonNull
1326     public static final Creator<VibratorInfo> CREATOR =
1327             new Creator<VibratorInfo>() {
1328                 @Override
1329                 public VibratorInfo createFromParcel(Parcel in) {
1330                     return new VibratorInfo(in);
1331                 }
1332 
1333                 @Override
1334                 public VibratorInfo[] newArray(int size) {
1335                     return new VibratorInfo[size];
1336                 }
1337             };
1338 }
1339