• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 static android.os.vibrator.Flags.FLAG_VENDOR_VIBRATION_EFFECTS;
20 
21 import android.annotation.DurationMillisLong;
22 import android.annotation.FlaggedApi;
23 import android.annotation.FloatRange;
24 import android.annotation.IntDef;
25 import android.annotation.IntRange;
26 import android.annotation.NonNull;
27 import android.annotation.Nullable;
28 import android.annotation.RequiresPermission;
29 import android.annotation.SystemApi;
30 import android.annotation.TestApi;
31 import android.compat.annotation.UnsupportedAppUsage;
32 import android.content.ContentResolver;
33 import android.content.Context;
34 import android.hardware.vibrator.IVibrator;
35 import android.hardware.vibrator.V1_0.EffectStrength;
36 import android.hardware.vibrator.V1_3.Effect;
37 import android.net.Uri;
38 import android.os.vibrator.BasicPwleSegment;
39 import android.os.vibrator.Flags;
40 import android.os.vibrator.PrebakedSegment;
41 import android.os.vibrator.PrimitiveSegment;
42 import android.os.vibrator.PwleSegment;
43 import android.os.vibrator.RampSegment;
44 import android.os.vibrator.StepSegment;
45 import android.os.vibrator.VibrationEffectSegment;
46 import android.os.vibrator.VibratorEnvelopeEffectInfo;
47 import android.os.vibrator.VibratorFrequencyProfileLegacy;
48 import android.util.MathUtils;
49 
50 import com.android.internal.util.Preconditions;
51 
52 import java.lang.annotation.Retention;
53 import java.lang.annotation.RetentionPolicy;
54 import java.time.Duration;
55 import java.util.ArrayList;
56 import java.util.Arrays;
57 import java.util.List;
58 import java.util.Locale;
59 import java.util.Objects;
60 import java.util.StringJoiner;
61 import java.util.function.BiFunction;
62 import java.util.function.Function;
63 
64 /**
65  * A VibrationEffect describes a haptic effect to be performed by a {@link Vibrator}.
66  *
67  * <p>These effects may be any number of things, from single shot vibrations to complex waveforms.
68  */
69 public abstract class VibrationEffect implements Parcelable {
70     private static final int PARCEL_TOKEN_COMPOSED = 1;
71     private static final int PARCEL_TOKEN_VENDOR_EFFECT = 2;
72 
73     // Stevens' coefficient to scale the perceived vibration intensity.
74     private static final float SCALE_GAMMA = 0.65f;
75     // If a vibration is playing for longer than 1s, it's probably not haptic feedback
76     private static final long MAX_HAPTIC_FEEDBACK_DURATION = 1000;
77     // If a vibration is playing more than 3 constants, it's probably not haptic feedback
78     private static final long MAX_HAPTIC_FEEDBACK_COMPOSITION_SIZE = 3;
79 
80     /**
81      * The default vibration strength of the device.
82      */
83     public static final int DEFAULT_AMPLITUDE = -1;
84 
85     /**
86      * The maximum amplitude value
87      * @hide
88      */
89     public static final int MAX_AMPLITUDE = 255;
90 
91     /**
92      * A click effect. Use this effect as a baseline, as it's the most common type of click effect.
93      */
94     public static final int EFFECT_CLICK = Effect.CLICK;
95 
96     /**
97      * A double click effect.
98      */
99     public static final int EFFECT_DOUBLE_CLICK = Effect.DOUBLE_CLICK;
100 
101     /**
102      * A tick effect. This effect is less strong compared to {@link #EFFECT_CLICK}.
103      */
104     public static final int EFFECT_TICK = Effect.TICK;
105 
106     /**
107      * A thud effect.
108      * @see #get(int)
109      * @hide
110      */
111     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
112     @TestApi
113     public static final int EFFECT_THUD = Effect.THUD;
114 
115     /**
116      * A pop effect.
117      * @see #get(int)
118      * @hide
119      */
120     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
121     @TestApi
122     public static final int EFFECT_POP = Effect.POP;
123 
124     /**
125      * A heavy click effect. This effect is stronger than {@link #EFFECT_CLICK}.
126      */
127     public static final int EFFECT_HEAVY_CLICK = Effect.HEAVY_CLICK;
128 
129     /**
130      * A texture effect meant to replicate soft ticks.
131      *
132      * <p>Unlike normal effects, texture effects are meant to be called repeatedly, generally in
133      * response to some motion, in order to replicate the feeling of some texture underneath the
134      * user's fingers.
135      *
136      * @see #get(int)
137      * @hide
138      */
139     @TestApi
140     public static final int EFFECT_TEXTURE_TICK = Effect.TEXTURE_TICK;
141 
142     /** {@hide} */
143     @TestApi
144     public static final int EFFECT_STRENGTH_LIGHT = EffectStrength.LIGHT;
145 
146     /** {@hide} */
147     @TestApi
148     public static final int EFFECT_STRENGTH_MEDIUM = EffectStrength.MEDIUM;
149 
150     /** {@hide} */
151     @TestApi
152     public static final int EFFECT_STRENGTH_STRONG = EffectStrength.STRONG;
153 
154     /**
155      * Ringtone patterns. They may correspond with the device's ringtone audio, or may just be a
156      * pattern that can be played as a ringtone with any audio, depending on the device.
157      *
158      * @see #get(Uri, Context)
159      * @hide
160      */
161     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
162     @TestApi
163     public static final int[] RINGTONES = {
164         Effect.RINGTONE_1,
165         Effect.RINGTONE_2,
166         Effect.RINGTONE_3,
167         Effect.RINGTONE_4,
168         Effect.RINGTONE_5,
169         Effect.RINGTONE_6,
170         Effect.RINGTONE_7,
171         Effect.RINGTONE_8,
172         Effect.RINGTONE_9,
173         Effect.RINGTONE_10,
174         Effect.RINGTONE_11,
175         Effect.RINGTONE_12,
176         Effect.RINGTONE_13,
177         Effect.RINGTONE_14,
178         Effect.RINGTONE_15
179     };
180 
181     /** @hide */
182     @IntDef(prefix = { "EFFECT_" }, value = {
183             EFFECT_TICK,
184             EFFECT_CLICK,
185             EFFECT_HEAVY_CLICK,
186             EFFECT_DOUBLE_CLICK,
187     })
188     @Retention(RetentionPolicy.SOURCE)
189     public @interface EffectType {}
190 
191     /** @hide to prevent subclassing from outside of the framework */
VibrationEffect()192     public VibrationEffect() { }
193 
194     /**
195      * Create a one shot vibration.
196      *
197      * <p>One shot vibrations will vibrate constantly for the specified period of time at the
198      * specified amplitude, and then stop.
199      *
200      * @param milliseconds The number of milliseconds to vibrate. This must be a positive number.
201      * @param amplitude The strength of the vibration. This must be a value between 1 and 255, or
202      * {@link #DEFAULT_AMPLITUDE}.
203      *
204      * @return The desired effect.
205      */
createOneShot(long milliseconds, int amplitude)206     public static VibrationEffect createOneShot(long milliseconds, int amplitude) {
207         if (amplitude == 0) {
208             throw new IllegalArgumentException(
209                     "amplitude must either be DEFAULT_AMPLITUDE, "
210                             + "or between 1 and 255 inclusive (amplitude=" + amplitude + ")");
211         }
212         return createWaveform(new long[]{milliseconds}, new int[]{amplitude}, -1 /* repeat */);
213     }
214 
215     /**
216      * Create a waveform vibration, using only off/on transitions at the provided time intervals,
217      * and potentially repeating.
218      *
219      * <p>In effect, the timings array represents the number of milliseconds <em>before</em> turning
220      * the vibrator on, followed by the number of milliseconds to keep the vibrator on, then
221      * the number of milliseconds turned off, and so on. Consequently, the first timing value will
222      * often be 0, so that the effect will start vibrating immediately.
223      *
224      * <p>This method is equivalent to calling {@link #createWaveform(long[], int[], int)} with
225      * corresponding amplitude values alternating between 0 and {@link #DEFAULT_AMPLITUDE},
226      * beginning with 0.
227      *
228      * <p>To cause the pattern to repeat, pass the index into the timings array at which to start
229      * the repetition, or -1 to disable repeating. Repeating effects will be played indefinitely
230      * and should be cancelled via {@link Vibrator#cancel()}.
231      *
232      * @param timings The pattern of alternating on-off timings, starting with an 'off' timing, and
233      *               representing the length of time to sustain the individual item (not
234      *               cumulative).
235      * @param repeat The index into the timings array at which to repeat, or -1 if you don't
236      *               want to repeat indefinitely.
237      *
238      * @return The desired effect.
239      */
createWaveform(long[] timings, int repeat)240     public static VibrationEffect createWaveform(long[] timings, int repeat) {
241         int[] amplitudes = new int[timings.length];
242         for (int i = 0; i < (timings.length / 2); i++) {
243             amplitudes[i*2 + 1] = VibrationEffect.DEFAULT_AMPLITUDE;
244         }
245         return createWaveform(timings, amplitudes, repeat);
246     }
247 
248     /**
249      * Computes a legacy vibration pattern (i.e. a pattern with duration values for "off/on"
250      * vibration components) that is equivalent to this VibrationEffect.
251      *
252      * <p>All non-repeating effects created with {@link #createWaveform(long[], int)} are
253      * convertible into an equivalent vibration pattern with this method. It is not guaranteed that
254      * an effect created with other means becomes converted into an equivalent legacy vibration
255      * pattern, even if it has an equivalent vibration pattern. If this method is unable to create
256      * an equivalent vibration pattern for such effects, it will return {@code null}.
257      *
258      * <p>Note that a valid equivalent long[] pattern cannot be created for an effect that has any
259      * form of repeating behavior, regardless of how the effect was created. For repeating effects,
260      * the method will always return {@code null}.
261      *
262      * @return a long array representing a vibration pattern equivalent to the VibrationEffect, if
263      *               the method successfully derived a vibration pattern equivalent to the effect
264      *               (this will always be the case if the effect was created via
265      *               {@link #createWaveform(long[], int)} and is non-repeating). Otherwise, returns
266      *               {@code null}.
267      * @hide
268      */
269     @TestApi
270     @Nullable
computeCreateWaveformOffOnTimingsOrNull()271     public abstract long[] computeCreateWaveformOffOnTimingsOrNull();
272 
273     /**
274      * Create a waveform vibration.
275      *
276      * <p>Waveform vibrations are a potentially repeating series of timing and amplitude pairs,
277      * provided in separate arrays. For each pair, the value in the amplitude array determines
278      * the strength of the vibration and the value in the timing array determines how long it
279      * vibrates for, in milliseconds.
280      *
281      * <p>To cause the pattern to repeat, pass the index into the timings array at which to start
282      * the repetition, or -1 to disable repeating. Repeating effects will be played indefinitely
283      * and should be cancelled via {@link Vibrator#cancel()}.
284      *
285      * @param timings The timing values, in milliseconds, of the timing / amplitude pairs. Timing
286      *                values of 0 will cause the pair to be ignored.
287      * @param amplitudes The amplitude values of the timing / amplitude pairs. Amplitude values
288      *                   must be between 0 and 255, or equal to {@link #DEFAULT_AMPLITUDE}. An
289      *                   amplitude value of 0 implies the motor is off.
290      * @param repeat The index into the timings array at which to repeat, or -1 if you don't
291      *               want to repeat indefinitely.
292      *
293      * @return The desired effect.
294      */
createWaveform(long[] timings, int[] amplitudes, int repeat)295     public static VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) {
296         if (timings.length != amplitudes.length) {
297             throw new IllegalArgumentException(
298                     "timing and amplitude arrays must be of equal length"
299                             + " (timings.length=" + timings.length
300                             + ", amplitudes.length=" + amplitudes.length + ")");
301         }
302         List<StepSegment> segments = new ArrayList<>();
303         for (int i = 0; i < timings.length; i++) {
304             float parsedAmplitude = amplitudes[i] == DEFAULT_AMPLITUDE
305                     ? DEFAULT_AMPLITUDE : (float) amplitudes[i] / MAX_AMPLITUDE;
306             segments.add(new StepSegment(parsedAmplitude, /* frequencyHz= */ 0, (int) timings[i]));
307         }
308         VibrationEffect effect = new Composed(segments, repeat);
309         effect.validate();
310         return effect;
311     }
312 
313     /**
314      * Create a predefined vibration effect.
315      *
316      * <p>Predefined effects are a set of common vibration effects that should be identical,
317      * regardless of the app they come from, in order to provide a cohesive experience for users
318      * across the entire device. They also may be custom tailored to the device hardware in order to
319      * provide a better experience than you could otherwise build using the generic building
320      * blocks.
321      *
322      * <p>This will fallback to a generic pattern if one exists and there does not exist a
323      * hardware-specific implementation of the effect.
324      *
325      * @param effectId The ID of the effect to perform:
326      *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
327      *
328      * @return The desired effect.
329      */
330     @NonNull
createPredefined(@ffectType int effectId)331     public static VibrationEffect createPredefined(@EffectType int effectId) {
332         return get(effectId, true);
333     }
334 
335     /**
336      * Create a vendor-defined vibration effect.
337      *
338      * <p>Vendor effects offer more flexibility for accessing vendor-specific vibrator capabilities,
339      * enabling control over any vibration parameter and more generic vibration waveforms for apps
340      * provided by the device vendor.
341      *
342      * <p>This requires hardware-specific implementation of the effect and will not have any
343      * platform fallback support.
344      *
345      * @param effect An opaque representation of the vibration effect which can also be serialized.
346      * @return The desired effect.
347      * @hide
348      */
349     @NonNull
350     @SystemApi
351     @FlaggedApi(FLAG_VENDOR_VIBRATION_EFFECTS)
352     @RequiresPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)
createVendorEffect(@onNull PersistableBundle effect)353     public static VibrationEffect createVendorEffect(@NonNull PersistableBundle effect) {
354         VibrationEffect vendorEffect = new VendorEffect(effect, VendorEffect.DEFAULT_STRENGTH,
355                 VendorEffect.DEFAULT_SCALE, VendorEffect.DEFAULT_SCALE);
356         vendorEffect.validate();
357         return vendorEffect;
358     }
359 
360     /**
361      * Get a predefined vibration effect.
362      *
363      * <p>Predefined effects are a set of common vibration effects that should be identical,
364      * regardless of the app they come from, in order to provide a cohesive experience for users
365      * across the entire device. They also may be custom tailored to the device hardware in order to
366      * provide a better experience than you could otherwise build using the generic building
367      * blocks.
368      *
369      * <p>This will fallback to a generic pattern if one exists and there does not exist a
370      * hardware-specific implementation of the effect.
371      *
372      * @param effectId The ID of the effect to perform:
373      *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
374      *
375      * @return The desired effect.
376      * @hide
377      */
378     @TestApi
get(int effectId)379     public static VibrationEffect get(int effectId) {
380         return get(effectId, PrebakedSegment.DEFAULT_SHOULD_FALLBACK);
381     }
382 
383     /**
384      * Get a predefined vibration effect.
385      *
386      * <p>Predefined effects are a set of common vibration effects that should be identical,
387      * regardless of the app they come from, in order to provide a cohesive experience for users
388      * across the entire device. They also may be custom tailored to the device hardware in order to
389      * provide a better experience than you could otherwise build using the generic building
390      * blocks.
391      *
392      * <p>Some effects you may only want to play if there's a hardware specific implementation
393      * because they may, for example, be too disruptive to the user without tuning. The
394      * {@code fallback} parameter allows you to decide whether you want to fallback to the generic
395      * implementation or only play if there's a tuned, hardware specific one available.
396      *
397      * @param effectId The ID of the effect to perform:
398      *                 {@link #EFFECT_CLICK}, {@link #EFFECT_DOUBLE_CLICK}, {@link #EFFECT_TICK}
399      * @param fallback Whether to fall back to a generic pattern if a hardware specific
400      *                 implementation doesn't exist.
401      *
402      * @return The desired effect.
403      * @hide
404      */
405     @TestApi
get(int effectId, boolean fallback)406     public static VibrationEffect get(int effectId, boolean fallback) {
407         VibrationEffect effect = new Composed(
408                 new PrebakedSegment(effectId, fallback, PrebakedSegment.DEFAULT_STRENGTH));
409         effect.validate();
410         return effect;
411     }
412 
413     /**
414      * Get a predefined vibration effect associated with a given URI.
415      *
416      * <p>Predefined effects are a set of common vibration effects that should be identical,
417      * regardless of the app they come from, in order to provide a cohesive experience for users
418      * across the entire device. They also may be custom tailored to the device hardware in order to
419      * provide a better experience than you could otherwise build using the generic building
420      * blocks.
421      *
422      * @param uri The URI associated with the haptic effect.
423      * @param context The context used to get the URI to haptic effect association.
424      *
425      * @return The desired effect, or {@code null} if there's no associated effect.
426      *
427      * @hide
428      */
429     @TestApi
430     @Nullable
get(Uri uri, Context context)431     public static VibrationEffect get(Uri uri, Context context) {
432         String[] uris = context.getResources().getStringArray(
433                 com.android.internal.R.array.config_ringtoneEffectUris);
434 
435         // Skip doing any IPC if we don't have any effects configured.
436         if (uris.length == 0) {
437             return null;
438         }
439 
440         final ContentResolver cr = context.getContentResolver();
441         Uri uncanonicalUri = cr.uncanonicalize(uri);
442         if (uncanonicalUri == null) {
443             // If we already had an uncanonical URI, it's possible we'll get null back here. In
444             // this case, just use the URI as passed in since it wasn't canonicalized in the first
445             // place.
446             uncanonicalUri = uri;
447         }
448 
449         for (int i = 0; i < uris.length && i < RINGTONES.length; i++) {
450             if (uris[i] == null) {
451                 continue;
452             }
453             Uri mappedUri = cr.uncanonicalize(Uri.parse(uris[i]));
454             if (mappedUri == null) {
455                 continue;
456             }
457             if (mappedUri.equals(uncanonicalUri)) {
458                 return get(RINGTONES[i]);
459             }
460         }
461         return null;
462     }
463 
464     /**
465      * Start composing a haptic effect.
466      *
467      * @see VibrationEffect.Composition
468      */
469     @NonNull
startComposition()470     public static Composition startComposition() {
471         return new Composition();
472     }
473 
474     /**
475      * Start building a waveform vibration.
476      *
477      * <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
478      * control over vibration amplitude and frequency via smooth transitions between values.
479      *
480      * <p>The waveform will start the first transition from the vibrator off state, with the
481      * resonant frequency by default. To provide an initial state, use
482      * {@link #startWaveform(VibrationEffect.VibrationParameter)}.
483      *
484      * @see VibrationEffect.WaveformBuilder
485      * @hide
486      */
487     @TestApi
488     @NonNull
startWaveform()489     public static WaveformBuilder startWaveform() {
490         return new WaveformBuilder();
491     }
492 
493     /**
494      * Start building a waveform vibration with an initial state specified by a
495      * {@link VibrationEffect.VibrationParameter}.
496      *
497      * <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
498      * control over vibration amplitude and frequency via smooth transitions between values.
499      *
500      * @param initialParameter The initial {@link VibrationEffect.VibrationParameter} value to be
501      *                         applied at the beginning of the vibration.
502      * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
503      *
504      * @see VibrationEffect.WaveformBuilder
505      * @hide
506      */
507     @TestApi
508     @NonNull
startWaveform(@onNull VibrationParameter initialParameter)509     public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter) {
510         WaveformBuilder builder = startWaveform();
511         builder.addTransition(Duration.ZERO, initialParameter);
512         return builder;
513     }
514 
515     /**
516      * Start building a waveform vibration with an initial state specified by two
517      * {@link VibrationEffect.VibrationParameter VibrationParameters}.
518      *
519      * <p>The waveform builder offers more flexibility for creating waveform vibrations, allowing
520      * control over vibration amplitude and frequency via smooth transitions between values.
521      *
522      * @param initialParameter1 The initial {@link VibrationEffect.VibrationParameter} value to be
523      *                          applied at the beginning of the vibration.
524      * @param initialParameter2 The initial {@link VibrationEffect.VibrationParameter} value to be
525      *                          applied at the beginning of the vibration, must be a different type
526      *                          of parameter than the one specified by the first argument.
527      * @return The {@link VibrationEffect.WaveformBuilder} started with the initial parameters.
528      *
529      * @see VibrationEffect.WaveformBuilder
530      * @hide
531      */
532     @TestApi
533     @NonNull
startWaveform(@onNull VibrationParameter initialParameter1, @NonNull VibrationParameter initialParameter2)534     public static WaveformBuilder startWaveform(@NonNull VibrationParameter initialParameter1,
535             @NonNull VibrationParameter initialParameter2) {
536         WaveformBuilder builder = startWaveform();
537         builder.addTransition(Duration.ZERO, initialParameter1, initialParameter2);
538         return builder;
539     }
540 
541     @Override
describeContents()542     public int describeContents() {
543         return 0;
544     }
545 
546     /** @hide */
validate()547     public abstract void validate();
548 
549 
550     /**
551      * If supported, truncate the length of this vibration effect to the provided length and return
552      * the result. Will always return null for repeating effects.
553      *
554      * @return The desired effect, or {@code null} if truncation is not applicable.
555      * @hide
556      */
557     @Nullable
cropToLengthOrNull(int length)558     public abstract VibrationEffect cropToLengthOrNull(int length);
559 
560     /**
561      * Gets the estimated duration of the vibration in milliseconds.
562      *
563      * <p>For effects without a defined end (e.g. a Waveform with a non-negative repeat index), this
564      * returns Long.MAX_VALUE. For effects with an unknown duration (e.g. predefined effects where
565      * the length is device and potentially run-time dependent), this returns -1.
566      *
567      * @hide
568      */
569     @TestApi
getDuration()570     public abstract long getDuration();
571 
572     /**
573      * Gets the estimated duration of the segment for given vibrator, in milliseconds.
574      *
575      * <p>For effects with hardware-dependent constants (e.g. primitive compositions), this returns
576      * the estimated duration based on the given {@link VibratorInfo}. For all other effects this
577      * will return the same as {@link #getDuration()}.
578      *
579      * @hide
580      */
getDuration(@ullable VibratorInfo vibratorInfo)581     public long getDuration(@Nullable VibratorInfo vibratorInfo) {
582         return getDuration();
583     }
584 
585     /**
586      * Checks if a vibrator with a given {@link VibratorInfo} can play this effect as intended.
587      *
588      * <p>See {@link VibratorInfo#areVibrationFeaturesSupported(VibrationEffect)} for more
589      * information about what counts as supported by a vibrator, and what counts as not.
590      *
591      * @hide
592      */
areVibrationFeaturesSupported(@onNull VibratorInfo vibratorInfo)593     public abstract boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo);
594 
595     /**
596      * Returns true if this effect could represent a touch haptic feedback.
597      *
598      * <p>It is strongly recommended that an instance of {@link VibrationAttributes} is specified
599      * for each vibration, with the correct usage. When a vibration is played with usage UNKNOWN,
600      * then this method will be used to classify the most common use case and make sure they are
601      * covered by the user settings for "Touch feedback".
602      *
603      * @hide
604      */
isHapticFeedbackCandidate()605     public boolean isHapticFeedbackCandidate() {
606         return false;
607     }
608 
609     /**
610      * Resolve default values into integer amplitude numbers.
611      *
612      * @param defaultAmplitude the default amplitude to apply, must be between 0 and
613      *                         MAX_AMPLITUDE
614      * @return this if amplitude value is already set, or a copy of this effect with given default
615      *         amplitude otherwise
616      *
617      * @hide
618      */
619     @NonNull
resolve(int defaultAmplitude)620     public abstract VibrationEffect resolve(int defaultAmplitude);
621 
622     /**
623      * Applies given effect strength to predefined and vendor-specific effects.
624      *
625      * @param effectStrength new effect strength to be applied, one of
626      *                       VibrationEffect.EFFECT_STRENGTH_*.
627      * @return this if there is no change, or a copy of this effect with new strength otherwise
628      * @hide
629      */
630     @NonNull
applyEffectStrength(int effectStrength)631     public abstract VibrationEffect applyEffectStrength(int effectStrength);
632 
633     /**
634      * Scale the vibration effect intensity with the given constraints.
635      *
636      * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
637      *                    scale down the intensity, values larger than 1 will scale up
638      * @return this if there is no scaling to be done, or a copy of this effect with scaled
639      *         vibration intensity otherwise
640      *
641      * @hide
642      */
643     @NonNull
scale(float scaleFactor)644     public abstract VibrationEffect scale(float scaleFactor);
645 
646     /**
647      * Performs a linear scaling on the effect intensity with the given factor.
648      *
649      * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
650      *                    scale down the intensity, values larger than 1 will scale up
651      * @return this if there is no scaling to be done, or a copy of this effect with scaled
652      *         vibration intensity otherwise
653      * @hide
654      */
655     @NonNull
applyAdaptiveScale(float scaleFactor)656     public abstract VibrationEffect applyAdaptiveScale(float scaleFactor);
657 
658     /**
659      * Ensures that the effect is repeating indefinitely or not. This is a lossy operation and
660      * should only be applied once to an original effect - it shouldn't be applied to the
661      * result of this method.
662      *
663      * <p>Non-repeating effects will be made repeating by looping the entire effect with the
664      * specified delay between each loop. The delay is added irrespective of whether the effect
665      * already has a delay at the beginning or end.
666      *
667      * <p>Repeating effects will be left with their native repeating portion if it should be
668      * repeating, and otherwise the loop index is removed, so that the entire effect plays once.
669      *
670      * @param wantRepeating Whether the effect is required to be repeating or not.
671      * @param loopDelayMs The milliseconds to pause between loops, if repeating is to be added to
672      *                    the effect. Ignored if {@code repeating==false} or the effect is already
673      *                    repeating itself. No delay is added if <= 0.
674      * @return this if the effect already satisfies the repeating requirement, or a copy of this
675      *         adjusted to repeat or not repeat as appropriate.
676      * @hide
677      */
678     @NonNull
applyRepeatingIndefinitely( boolean wantRepeating, int loopDelayMs)679     public abstract VibrationEffect applyRepeatingIndefinitely(
680             boolean wantRepeating, int loopDelayMs);
681 
682     /**
683      * Scale given vibration intensity by the given factor.
684      *
685      * <p> This scale is not necessarily linear and may apply a gamma correction to the scale
686      * factor before using it.
687      *
688      * @param intensity   relative intensity of the effect, must be between 0 and 1
689      * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
690      *                    scale down the intensity, values larger than 1 will scale up
691      * @return the scaled intensity which will be values within [0, 1].
692      *
693      * @hide
694      */
scale(float intensity, float scaleFactor)695     public static float scale(float intensity, float scaleFactor) {
696         if (Flags.hapticsScaleV2Enabled()) {
697             if (Float.compare(scaleFactor, 1) <= 0 || Float.compare(intensity, 0) == 0) {
698                 // Scaling down or scaling zero intensity is straightforward.
699                 return scaleFactor * intensity;
700             }
701             // Using S * x / (1 + (S - 1) * x^2) as the scale up function to converge to 1.0.
702             return (scaleFactor * intensity) / (1 + (scaleFactor - 1) * intensity * intensity);
703         }
704 
705         // Applying gamma correction to the scale factor, which is the same as encoding the input
706         // value, scaling it, then decoding the scaled value.
707         float scale = MathUtils.pow(scaleFactor, 1f / SCALE_GAMMA);
708 
709         if (scaleFactor <= 1) {
710             // Scale down is simply a gamma corrected application of scaleFactor to the intensity.
711             // Scale up requires a different curve to ensure the intensity will not become > 1.
712             return intensity * scale;
713         }
714 
715         // Apply the scale factor a few more times to make the ramp curve closer to the raw scale.
716         float extraScale = MathUtils.pow(scaleFactor, 4f - scaleFactor);
717         float x = intensity * scale * extraScale;
718         float maxX = scale * extraScale; // scaled x for intensity == 1
719 
720         float expX = MathUtils.exp(x);
721         float expMaxX = MathUtils.exp(maxX);
722 
723         // Using f = tanh as the scale up function so the max value will converge.
724         // a = 1/f(maxX), used to scale f so that a*f(maxX) = 1 (the value will converge to 1).
725         float a = (expMaxX + 1f) / (expMaxX - 1f);
726         float fx = (expX - 1f) / (expX + 1f);
727 
728         return MathUtils.constrain(a * fx, 0f, 1f);
729     }
730 
731     /**
732      * Performs a linear scaling on the given vibration intensity by the given factor.
733      *
734      * @param intensity relative intensity of the effect, must be between 0 and 1.
735      * @param scaleFactor scale factor to be applied to the intensity. Values within [0,1) will
736      *                    scale down the intensity, values larger than 1 will scale up.
737      * @return the scaled intensity which will be values within [0, 1].
738      *
739      * @hide
740      */
scaleLinearly(float intensity, float scaleFactor)741     public static float scaleLinearly(float intensity, float scaleFactor) {
742         return MathUtils.constrain(intensity * scaleFactor, 0f, 1f);
743     }
744 
745     /**
746      * Returns a compact version of the {@link #toString()} result for debugging purposes.
747      *
748      * @hide
749      */
toDebugString()750     public abstract String toDebugString();
751 
752     /** @hide */
effectIdToString(int effectId)753     public static String effectIdToString(int effectId) {
754         return switch (effectId) {
755             case EFFECT_CLICK -> "CLICK";
756             case EFFECT_TICK -> "TICK";
757             case EFFECT_HEAVY_CLICK -> "HEAVY_CLICK";
758             case EFFECT_DOUBLE_CLICK -> "DOUBLE_CLICK";
759             case EFFECT_POP -> "POP";
760             case EFFECT_THUD -> "THUD";
761             case EFFECT_TEXTURE_TICK -> "TEXTURE_TICK";
762             default -> Integer.toString(effectId);
763         };
764     }
765 
766     /** @hide */
effectStrengthToString(int effectStrength)767     public static String effectStrengthToString(int effectStrength) {
768         return switch (effectStrength) {
769             case EFFECT_STRENGTH_LIGHT -> "LIGHT";
770             case EFFECT_STRENGTH_MEDIUM -> "MEDIUM";
771             case EFFECT_STRENGTH_STRONG -> "STRONG";
772             default -> Integer.toString(effectStrength);
773         };
774     }
775 
776     /**
777      * Transforms a {@link VibrationEffect} using a generic parameter.
778      *
779      * <p>This can be used for scaling effects based on user settings or adapting them to the
780      * capabilities of a specific device vibrator.
781      *
782      * @param <ParamT> The type of parameter to be used on the effect by this transformation
783      * @hide
784      */
785     public interface Transformation<ParamT> {
786 
787         /** Transforms given effect by applying the given parameter. */
788         @NonNull
789         VibrationEffect transform(@NonNull VibrationEffect effect, @NonNull ParamT param);
790     }
791 
792     /**
793      * Implementation of {@link VibrationEffect} described by a composition of one or more
794      * {@link VibrationEffectSegment}, with an optional index to represent repeating effects.
795      *
796      * @hide
797      */
798     @TestApi
799     public static final class Composed extends VibrationEffect {
800         private final ArrayList<VibrationEffectSegment> mSegments;
801         private final int mRepeatIndex;
802 
803         /** @hide */
804         Composed(@NonNull Parcel in) {
805             this(Objects.requireNonNull(in.readArrayList(
806                             VibrationEffectSegment.class.getClassLoader(),
807                             VibrationEffectSegment.class)),
808                     in.readInt());
809         }
810 
811         /** @hide */
812         Composed(@NonNull VibrationEffectSegment segment) {
813             this(Arrays.asList(segment), /* repeatIndex= */ -1);
814         }
815 
816         /** @hide */
817         public Composed(@NonNull List<? extends VibrationEffectSegment> segments, int repeatIndex) {
818             super();
819             mSegments = new ArrayList<>(segments);
820             mRepeatIndex = repeatIndex;
821         }
822 
823         @NonNull
824         public List<VibrationEffectSegment> getSegments() {
825             return mSegments;
826         }
827 
828         public int getRepeatIndex() {
829             return mRepeatIndex;
830         }
831 
832          /** @hide */
833         @Override
834         @Nullable
835         public long[] computeCreateWaveformOffOnTimingsOrNull() {
836             if (getRepeatIndex() >= 0) {
837                 // Repeating effects cannot be fully represented as a long[] legacy pattern.
838                 return null;
839             }
840 
841             List<VibrationEffectSegment> segments = getSegments();
842 
843             // The maximum possible size of the final pattern is 1 plus the number of segments in
844             // the original effect. This is because we will add an empty "off" segment at the
845             // start of the pattern if the first segment of the original effect is an "on" segment.
846             // (because the legacy patterns start with an "off" pattern). Other than this one case,
847             // we will add the durations of back-to-back segments of similar amplitudes (amplitudes
848             // that are all "on" or "off") and create a pattern entry for the total duration, which
849             // will not take more number pattern entries than the number of segments processed.
850             long[] patternBuffer = new long[segments.size() + 1];
851             int patternIndex = 0;
852 
853             for (int i = 0; i < segments.size(); i++) {
854                 StepSegment stepSegment =
855                         castToValidStepSegmentForOffOnTimingsOrNull(segments.get(i));
856                 if (stepSegment == null) {
857                     // This means that there is 1 or more segments of this effect that is/are not a
858                     // possible component of a legacy vibration pattern. Thus, the VibrationEffect
859                     // does not have any equivalent legacy vibration pattern.
860                     return null;
861                 }
862 
863                 boolean isSegmentOff = stepSegment.getAmplitude() == 0;
864                 // Even pattern indices are "off", and odd pattern indices are "on"
865                 boolean isCurrentPatternIndexOff = (patternIndex % 2) == 0;
866                 if (isSegmentOff != isCurrentPatternIndexOff) {
867                     // Move the pattern index one step ahead, so that the current segment's
868                     // "off"/"on" property matches that of the index's
869                     ++patternIndex;
870                 }
871                 patternBuffer[patternIndex] += stepSegment.getDuration();
872             }
873 
874             return Arrays.copyOf(patternBuffer, patternIndex + 1);
875         }
876 
877         /** @hide */
878         @Override
879         public void validate() {
880             int segmentCount = mSegments.size();
881             boolean hasNonZeroDuration = false;
882             for (int i = 0; i < segmentCount; i++) {
883                 VibrationEffectSegment segment = mSegments.get(i);
884                 segment.validate();
885                 // A segment with unknown duration = -1 still counts as a non-zero duration.
886                 hasNonZeroDuration |= segment.getDuration() != 0;
887             }
888             if (!hasNonZeroDuration) {
889                 throw new IllegalArgumentException("at least one timing must be non-zero"
890                         + " (segments=" + mSegments + ")");
891             }
892             if (mRepeatIndex != -1) {
893                 Preconditions.checkArgumentInRange(mRepeatIndex, 0, segmentCount - 1,
894                         "repeat index must be within the bounds of the segments (segments.length="
895                                 + segmentCount + ", index=" + mRepeatIndex + ")");
896             }
897         }
898 
899         /** @hide */
900         @Override
901         @Nullable
902         public VibrationEffect cropToLengthOrNull(int length) {
903             // drop repeating effects
904             if (mRepeatIndex >= 0) {
905                 return null;
906             }
907 
908             int segmentCount = mSegments.size();
909             if (segmentCount <= length) {
910                 return this;
911             }
912 
913             ArrayList truncated = new ArrayList(mSegments.subList(0, length));
914             Composed updated = new Composed(truncated, mRepeatIndex);
915             try {
916                 updated.validate();
917             } catch (IllegalArgumentException e) {
918                 return null;
919             }
920             return updated;
921         }
922 
923         @Override
924         public long getDuration() {
925             return getDuration(VibrationEffectSegment::getDuration);
926         }
927 
928         /** @hide */
929         @Override
930         public long getDuration(@Nullable VibratorInfo vibratorInfo) {
931             return getDuration(segment -> segment.getDuration(vibratorInfo));
932         }
933 
934         private long getDuration(Function<VibrationEffectSegment, Long> durationFn) {
935             if (mRepeatIndex >= 0) {
936                 return Long.MAX_VALUE;
937             }
938             int segmentCount = mSegments.size();
939             long totalDuration = 0;
940             for (int i = 0; i < segmentCount; i++) {
941                 long segmentDuration = durationFn.apply(mSegments.get(i));
942                 if (segmentDuration < 0) {
943                     return segmentDuration;
944                 }
945                 totalDuration += segmentDuration;
946             }
947             return totalDuration;
948         }
949 
950         /** @hide */
951         @Override
952         public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
953             for (VibrationEffectSegment segment : mSegments) {
954                 if (!segment.areVibrationFeaturesSupported(vibratorInfo)) {
955                     return false;
956                 }
957             }
958             return true;
959         }
960 
961         /** @hide */
962         @Override
963         public boolean isHapticFeedbackCandidate() {
964             long totalDuration = getDuration();
965             if (totalDuration > MAX_HAPTIC_FEEDBACK_DURATION) {
966                 // Vibration duration is known and is longer than the max duration used to classify
967                 // haptic feedbacks (or repeating indefinitely with duration == Long.MAX_VALUE).
968                 return false;
969             }
970             int segmentCount = mSegments.size();
971             if (segmentCount > MAX_HAPTIC_FEEDBACK_COMPOSITION_SIZE) {
972                 // Vibration has some predefined or primitive constants, it should be limited to the
973                 // max composition size used to classify haptic feedbacks.
974                 return false;
975             }
976             totalDuration = 0;
977             for (int i = 0; i < segmentCount; i++) {
978                 if (!mSegments.get(i).isHapticFeedbackCandidate()) {
979                     // There is at least one segment that is not a candidate for a haptic feedback.
980                     return false;
981                 }
982                 long segmentDuration = mSegments.get(i).getDuration();
983                 if (segmentDuration > 0) {
984                     totalDuration += segmentDuration;
985                 }
986             }
987             // Vibration might still have some ramp or step segments, check the known duration.
988             return totalDuration <= MAX_HAPTIC_FEEDBACK_DURATION;
989         }
990 
991         /** @hide */
992         @NonNull
993         @Override
994         public Composed resolve(int defaultAmplitude) {
995             return applyToSegments(VibrationEffectSegment::resolve, defaultAmplitude);
996         }
997 
998         /** @hide */
999         @NonNull
1000         @Override
1001         public VibrationEffect applyEffectStrength(int effectStrength) {
1002             return applyToSegments(VibrationEffectSegment::applyEffectStrength, effectStrength);
1003         }
1004 
1005         /** @hide */
1006         @NonNull
1007         @Override
1008         public Composed scale(float scaleFactor) {
1009             return applyToSegments(VibrationEffectSegment::scale, scaleFactor);
1010         }
1011 
1012         /** @hide */
1013         @NonNull
1014         @Override
1015         public Composed applyAdaptiveScale(float scaleFactor) {
1016             return applyToSegments(VibrationEffectSegment::scaleLinearly, scaleFactor);
1017         }
1018 
1019         /** @hide */
1020         @NonNull
1021         @Override
1022         public Composed applyRepeatingIndefinitely(boolean wantRepeating, int loopDelayMs) {
1023             boolean isRepeating = mRepeatIndex >= 0;
1024             if (isRepeating == wantRepeating) {
1025                 return this;
1026             } else if (!wantRepeating) {
1027                 return new Composed(mSegments, -1);
1028             } else if (loopDelayMs <= 0) {
1029                 // Loop with no delay: repeat at index zero.
1030                 return new Composed(mSegments, 0);
1031             } else {
1032                 // Append a delay and loop. It doesn't matter that there's a delay on the
1033                 // end because the looping is always indefinite until cancelled.
1034                 ArrayList<VibrationEffectSegment> loopingSegments =
1035                         new ArrayList<>(mSegments.size() + 1);
1036                 loopingSegments.addAll(mSegments);
1037                 loopingSegments.add(
1038                         new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0, loopDelayMs));
1039                 return new Composed(loopingSegments, 0);
1040             }
1041         }
1042 
1043         @Override
1044         public boolean equals(@Nullable Object o) {
1045             if (this == o) {
1046                 return true;
1047             }
1048             if (!(o instanceof Composed other)) {
1049                 return false;
1050             }
1051             return mSegments.equals(other.mSegments) && mRepeatIndex == other.mRepeatIndex;
1052         }
1053 
1054         @Override
1055         public int hashCode() {
1056             return Objects.hash(mSegments, mRepeatIndex);
1057         }
1058 
1059         @Override
1060         public String toString() {
1061             return "Composed{segments=" + mSegments
1062                     + ", repeat=" + mRepeatIndex
1063                     + "}";
1064         }
1065 
1066         /** @hide */
1067         @Override
1068         public String toDebugString() {
1069             if (mSegments.size() == 1 && mRepeatIndex < 0) {
1070                 // Simplify effect string, use the single segment to represent it.
1071                 return mSegments.get(0).toDebugString();
1072             }
1073             StringJoiner sj = new StringJoiner(",", "[", "]");
1074             for (int i = 0; i < mSegments.size(); i++) {
1075                 sj.add(mSegments.get(i).toDebugString());
1076             }
1077             if (mRepeatIndex >= 0) {
1078                 return String.format(Locale.ROOT, "%s, repeat=%d", sj, mRepeatIndex);
1079             }
1080             return sj.toString();
1081         }
1082 
1083         @Override
1084         public int describeContents() {
1085             return 0;
1086         }
1087 
1088         @Override
1089         public void writeToParcel(@NonNull Parcel out, int flags) {
1090             out.writeInt(PARCEL_TOKEN_COMPOSED);
1091             out.writeList(mSegments);
1092             out.writeInt(mRepeatIndex);
1093         }
1094 
1095         @NonNull
1096         public static final Creator<Composed> CREATOR =
1097                 new Creator<Composed>() {
1098                     @Override
1099                     public Composed createFromParcel(Parcel in) {
1100                         in.readInt(); // Skip the parcel type token
1101                         return new Composed(in);
1102                     }
1103 
1104                     @Override
1105                     public Composed[] newArray(int size) {
1106                         return new Composed[size];
1107                     }
1108                 };
1109 
1110         /**
1111          * Casts a provided {@link VibrationEffectSegment} to a {@link StepSegment} and returns it,
1112          * only if it can possibly be a segment for an effect created via
1113          * {@link #createWaveform(long[], int)}. Otherwise, returns {@code null}.
1114          */
1115         @Nullable
1116         private static StepSegment castToValidStepSegmentForOffOnTimingsOrNull(
1117                 VibrationEffectSegment segment) {
1118             if (!(segment instanceof StepSegment)) {
1119                 return null;
1120             }
1121 
1122             StepSegment stepSegment = (StepSegment) segment;
1123             if (stepSegment.getFrequencyHz() != 0) {
1124                 return null;
1125             }
1126 
1127             float amplitude = stepSegment.getAmplitude();
1128             if (amplitude != 0 && amplitude != DEFAULT_AMPLITUDE) {
1129                 return null;
1130             }
1131 
1132             return stepSegment;
1133         }
1134 
1135         private <T> Composed applyToSegments(
1136                 BiFunction<VibrationEffectSegment, T, VibrationEffectSegment> function, T param) {
1137             int segmentCount = mSegments.size();
1138             ArrayList<VibrationEffectSegment> updatedSegments = new ArrayList<>(segmentCount);
1139             for (int i = 0; i < segmentCount; i++) {
1140                 updatedSegments.add(function.apply(mSegments.get(i), param));
1141             }
1142             if (mSegments.equals(updatedSegments)) {
1143                 return this;
1144             }
1145             Composed updated = new Composed(updatedSegments, mRepeatIndex);
1146             updated.validate();
1147             return updated;
1148         }
1149     }
1150 
1151     /**
1152      * Implementation of {@link VibrationEffect} described by a generic {@link PersistableBundle}
1153      * defined by vendors.
1154      *
1155      * @hide
1156      */
1157     @TestApi
1158     @FlaggedApi(FLAG_VENDOR_VIBRATION_EFFECTS)
1159     public static final class VendorEffect extends VibrationEffect {
1160         /** @hide */
1161         public static final int DEFAULT_STRENGTH = VibrationEffect.EFFECT_STRENGTH_MEDIUM;
1162         /** @hide */
1163         public static final float DEFAULT_SCALE = 1.0f;
1164 
1165         private final PersistableBundle mVendorData;
1166         private final int mEffectStrength;
1167         private final float mScale;
1168         private final float mAdaptiveScale;
1169 
1170         /** @hide */
1171         VendorEffect(@NonNull Parcel in) {
1172             this(Objects.requireNonNull(
1173                     in.readPersistableBundle(VibrationEffect.class.getClassLoader())),
1174                     in.readInt(), in.readFloat(), in.readFloat());
1175         }
1176 
1177         /** @hide */
1178         public VendorEffect(@NonNull PersistableBundle vendorData, int effectStrength,
1179                 float scale, float adaptiveScale) {
1180             mVendorData = vendorData;
1181             mEffectStrength = effectStrength;
1182             mScale = scale;
1183             mAdaptiveScale = adaptiveScale;
1184         }
1185 
1186         @NonNull
1187         public PersistableBundle getVendorData() {
1188             return mVendorData;
1189         }
1190 
1191         public int getEffectStrength() {
1192             return mEffectStrength;
1193         }
1194 
1195         public float getScale() {
1196             return mScale;
1197         }
1198 
1199         public float getAdaptiveScale() {
1200             return mAdaptiveScale;
1201         }
1202 
1203         /** @hide */
1204         @Override
1205         @Nullable
1206         public long[] computeCreateWaveformOffOnTimingsOrNull() {
1207             return null;
1208         }
1209 
1210         /** @hide */
1211         @Override
1212         public void validate() {
1213             Preconditions.checkArgument(!mVendorData.isEmpty(),
1214                     "Vendor effect bundle must be non-empty");
1215         }
1216 
1217         /** @hide */
1218         @Override
1219         @Nullable
1220         public VibrationEffect cropToLengthOrNull(int length) {
1221             return null;
1222         }
1223 
1224         @Override
1225         public long getDuration() {
1226             return -1; // UNKNOWN
1227         }
1228 
1229         /** @hide */
1230         @Override
1231         public boolean areVibrationFeaturesSupported(@NonNull VibratorInfo vibratorInfo) {
1232             return vibratorInfo.hasCapability(IVibrator.CAP_PERFORM_VENDOR_EFFECTS);
1233         }
1234 
1235         /** @hide */
1236         @Override
1237         public boolean isHapticFeedbackCandidate() {
1238             return false;
1239         }
1240 
1241         /** @hide */
1242         @NonNull
1243         @Override
1244         public VendorEffect resolve(int defaultAmplitude) {
1245             return this;
1246         }
1247 
1248         /** @hide */
1249         @NonNull
1250         @Override
1251         public VibrationEffect applyEffectStrength(int effectStrength) {
1252             if (mEffectStrength == effectStrength) {
1253                 return this;
1254             }
1255             VendorEffect updated = new VendorEffect(mVendorData, effectStrength, mScale,
1256                     mAdaptiveScale);
1257             updated.validate();
1258             return updated;
1259         }
1260 
1261         /** @hide */
1262         @NonNull
1263         @Override
1264         public VendorEffect scale(float scaleFactor) {
1265             if (Float.compare(mScale, scaleFactor) == 0) {
1266                 return this;
1267             }
1268             VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, scaleFactor,
1269                     mAdaptiveScale);
1270             updated.validate();
1271             return updated;
1272         }
1273 
1274         /** @hide */
1275         @NonNull
1276         @Override
1277         public VibrationEffect applyAdaptiveScale(float scaleFactor) {
1278             if (Float.compare(mAdaptiveScale, scaleFactor) == 0) {
1279                 return this;
1280             }
1281             VendorEffect updated = new VendorEffect(mVendorData, mEffectStrength, mScale,
1282                     scaleFactor);
1283             updated.validate();
1284             return updated;
1285         }
1286 
1287         /** @hide */
1288         @NonNull
1289         @Override
1290         public VendorEffect applyRepeatingIndefinitely(boolean wantRepeating, int loopDelayMs) {
1291             return this;
1292         }
1293 
1294         @Override
1295         public boolean equals(@Nullable Object o) {
1296             if (this == o) {
1297                 return true;
1298             }
1299             if (!(o instanceof VendorEffect other)) {
1300                 return false;
1301             }
1302             return mEffectStrength == other.mEffectStrength
1303                     && (Float.compare(mScale, other.mScale) == 0)
1304                     && (Float.compare(mAdaptiveScale, other.mAdaptiveScale) == 0)
1305                     && isPersistableBundleEquals(mVendorData, other.mVendorData);
1306         }
1307 
1308         @Override
1309         public int hashCode() {
1310             // PersistableBundle does not implement hashCode, so use its size as a shortcut.
1311             return Objects.hash(mVendorData.size(), mEffectStrength, mScale, mAdaptiveScale);
1312         }
1313 
1314         @Override
1315         public String toString() {
1316             return String.format(Locale.ROOT,
1317                     "VendorEffect{vendorData=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f}",
1318                     mVendorData, effectStrengthToString(mEffectStrength), mScale, mAdaptiveScale);
1319         }
1320 
1321         /** @hide */
1322         @Override
1323         public String toDebugString() {
1324             return String.format(Locale.ROOT,
1325                     "vendorEffect=%s, strength=%s, scale=%.2f, adaptiveScale=%.2f",
1326                     mVendorData.toShortString(), effectStrengthToString(mEffectStrength),
1327                     mScale, mAdaptiveScale);
1328         }
1329 
1330         @Override
1331         public int describeContents() {
1332             return 0;
1333         }
1334 
1335         @Override
1336         public void writeToParcel(@NonNull Parcel out, int flags) {
1337             out.writeInt(PARCEL_TOKEN_VENDOR_EFFECT);
1338             out.writePersistableBundle(mVendorData);
1339             out.writeInt(mEffectStrength);
1340             out.writeFloat(mScale);
1341             out.writeFloat(mAdaptiveScale);
1342         }
1343 
1344         /**
1345          * Compares two {@link PersistableBundle} objects are equals.
1346          */
1347         private static boolean isPersistableBundleEquals(
1348                 PersistableBundle first, PersistableBundle second) {
1349             if (first == second) {
1350                 return true;
1351             }
1352             if (first == null || second == null || first.size() != second.size()) {
1353                 return false;
1354             }
1355             for (String key : first.keySet()) {
1356                 if (!isPersistableBundleSupportedValueEquals(first.get(key), second.get(key))) {
1357                     return false;
1358                 }
1359             }
1360             return true;
1361         }
1362 
1363         /**
1364          * Compares two values which type is supported by {@link PersistableBundle}.
1365          *
1366          * <p>If the type isn't supported. The equality is done by {@link Object#equals(Object)}.
1367          */
1368         private static boolean isPersistableBundleSupportedValueEquals(
1369                 Object first, Object second) {
1370             if (first == second) {
1371                 return true;
1372             } else if (first == null || second == null
1373                     || !first.getClass().equals(second.getClass())) {
1374                 return false;
1375             } else if (first instanceof PersistableBundle) {
1376                 return isPersistableBundleEquals(
1377                         (PersistableBundle) first, (PersistableBundle) second);
1378             } else if (first instanceof int[]) {
1379                 return Arrays.equals((int[]) first, (int[]) second);
1380             } else if (first instanceof long[]) {
1381                 return Arrays.equals((long[]) first, (long[]) second);
1382             } else if (first instanceof double[]) {
1383                 return Arrays.equals((double[]) first, (double[]) second);
1384             } else if (first instanceof boolean[]) {
1385                 return Arrays.equals((boolean[]) first, (boolean[]) second);
1386             } else if (first instanceof String[]) {
1387                 return Arrays.equals((String[]) first, (String[]) second);
1388             } else {
1389                 return Objects.equals(first, second);
1390             }
1391         }
1392 
1393         @NonNull
1394         public static final Creator<VendorEffect> CREATOR =
1395                 new Creator<VendorEffect>() {
1396                     @Override
1397                     public VendorEffect createFromParcel(Parcel in) {
1398                         in.readInt(); // Skip the parcel type token
1399                         return new VendorEffect(in);
1400                     }
1401 
1402                     @Override
1403                     public VendorEffect[] newArray(int size) {
1404                         return new VendorEffect[size];
1405                     }
1406                 };
1407     }
1408 
1409     /**
1410      * Creates a new {@link VibrationEffect} that repeats the given effect indefinitely.
1411      *
1412      * <p>The input vibration must not be a repeating vibration. If it is, an
1413      * {@link IllegalArgumentException} will be thrown.
1414      *
1415      * @param effect The {@link VibrationEffect} that will be repeated.
1416      * @return A {@link VibrationEffect} that repeats the effect indefinitely.
1417      * @throws IllegalArgumentException if the effect is already a repeating vibration.
1418      */
1419     @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
1420     @NonNull
1421     public static VibrationEffect createRepeatingEffect(@NonNull VibrationEffect effect) {
1422         return VibrationEffect.startComposition()
1423                 .repeatEffectIndefinitely(effect)
1424                 .compose();
1425     }
1426 
1427     /**
1428      * Creates a new {@link VibrationEffect} by merging the preamble and repeating vibration effect.
1429      *
1430      * <p>Neither input vibration may already be repeating. An {@link IllegalArgumentException} will
1431      * be thrown if either input vibration is set to repeat indefinitely.
1432      *
1433      * @param preamble        The starting vibration effect, which must be finite.
1434      * @param repeatingEffect The vibration effect to be repeated indefinitely after the preamble.
1435      * @return A {@link VibrationEffect} that plays the preamble once followed by the
1436      * `repeatingEffect` indefinitely.
1437      * @throws IllegalArgumentException if either preamble or repeatingEffect is already a repeating
1438      *                                  vibration.
1439      */
1440     @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
1441     @NonNull
1442     public static VibrationEffect createRepeatingEffect(@NonNull VibrationEffect preamble,
1443             @NonNull VibrationEffect repeatingEffect) {
1444         Preconditions.checkArgument(preamble.getDuration() < Long.MAX_VALUE,
1445                 "Can't repeat an indefinitely repeating effect.");
1446         return VibrationEffect.startComposition()
1447                 .addEffect(preamble)
1448                 .repeatEffectIndefinitely(repeatingEffect)
1449                 .compose();
1450     }
1451 
1452     /**
1453      * A composition of haptic elements that are combined to be playable as a single
1454      * {@link VibrationEffect}.
1455      *
1456      * <p>The haptic primitives are available as {@code Composition.PRIMITIVE_*} constants and
1457      * can be added to a composition to create a custom vibration effect. Here is an example of an
1458      * effect that grows in intensity and then dies off, with a longer rising portion for emphasis
1459      * and an extra tick 100ms after:
1460      *
1461      * <pre>
1462      * {@code VibrationEffect effect = VibrationEffect.startComposition()
1463      *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 0.5f)
1464      *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_FALL, 0.5f)
1465      *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 1.0f, 100)
1466      *     .compose();}</pre>
1467      *
1468      * <p>When choosing to play a composed effect, you should check that individual components are
1469      * supported by the device by using {@link Vibrator#arePrimitivesSupported}.
1470      *
1471      * @see VibrationEffect#startComposition()
1472      */
1473     public static final class Composition {
1474         /** @hide */
1475         @IntDef(prefix = { "PRIMITIVE_" }, value = {
1476                 PRIMITIVE_CLICK,
1477                 PRIMITIVE_THUD,
1478                 PRIMITIVE_SPIN,
1479                 PRIMITIVE_QUICK_RISE,
1480                 PRIMITIVE_SLOW_RISE,
1481                 PRIMITIVE_QUICK_FALL,
1482                 PRIMITIVE_TICK,
1483                 PRIMITIVE_LOW_TICK,
1484         })
1485         @Retention(RetentionPolicy.SOURCE)
1486         public @interface PrimitiveType {
1487         }
1488 
1489         /** @hide */
1490         @IntDef(prefix = { "DELAY_TYPE_" }, value = {
1491                 DELAY_TYPE_PAUSE,
1492                 DELAY_TYPE_RELATIVE_START_OFFSET,
1493         })
1494         @Retention(RetentionPolicy.SOURCE)
1495         public @interface DelayType {
1496         }
1497 
1498         /**
1499          * Exception thrown when adding an element to a {@link Composition} that already ends in an
1500          * indefinitely repeating effect.
1501          * @hide
1502          */
1503         @TestApi
1504         public static final class UnreachableAfterRepeatingIndefinitelyException
1505                 extends IllegalStateException {
1506             UnreachableAfterRepeatingIndefinitelyException() {
1507                 super("Compositions ending in an indefinitely repeating effect can't be extended");
1508             }
1509         }
1510 
1511         /**
1512          * No haptic effect. Used to generate extended delays between primitives.
1513          *
1514          * @hide
1515          */
1516         public static final int PRIMITIVE_NOOP = 0;
1517         /**
1518          * This effect should produce a sharp, crisp click sensation.
1519          */
1520         public static final int PRIMITIVE_CLICK = 1;
1521         /**
1522          * A haptic effect that simulates downwards movement with gravity. Often
1523          * followed by extra energy of hitting and reverberation to augment
1524          * physicality.
1525          */
1526         public static final int PRIMITIVE_THUD = 2;
1527         /**
1528          * A haptic effect that simulates spinning momentum.
1529          */
1530         public static final int PRIMITIVE_SPIN = 3;
1531         /**
1532          * A haptic effect that simulates quick upward movement against gravity.
1533          */
1534         public static final int PRIMITIVE_QUICK_RISE = 4;
1535         /**
1536          * A haptic effect that simulates slow upward movement against gravity.
1537          */
1538         public static final int PRIMITIVE_SLOW_RISE = 5;
1539         /**
1540          * A haptic effect that simulates quick downwards movement with gravity.
1541          */
1542         public static final int PRIMITIVE_QUICK_FALL = 6;
1543         /**
1544          * This very short effect should produce a light crisp sensation intended
1545          * to be used repetitively for dynamic feedback.
1546          */
1547         // Internally this maps to the HAL constant CompositePrimitive::LIGHT_TICK
1548         public static final int PRIMITIVE_TICK = 7;
1549         /**
1550          * This very short low frequency effect should produce a light crisp sensation
1551          * intended to be used repetitively for dynamic feedback.
1552          */
1553         // Internally this maps to the HAL constant CompositePrimitive::LOW_TICK
1554         public static final int PRIMITIVE_LOW_TICK = 8;
1555 
1556         /**
1557          * The delay represents a pause in the composition between the end of the previous primitive
1558          * and the beginning of the next one.
1559          *
1560          * <p>The primitive will start after the requested pause after the last primitive ended.
1561          * The actual time the primitive will be played depends on the previous primitive's actual
1562          * duration on the device hardware. This enables the combination of primitives to create
1563          * more complex effects based on how close to each other they'll play. Here is an example:
1564          *
1565          * <pre>
1566          *     VibrationEffect popEffect = VibrationEffect.startComposition()
1567          *         .addPrimitive(PRIMITIVE_QUICK_RISE)
1568          *         .addPrimitive(PRIMITIVE_CLICK, 0.7, 50, DELAY_TYPE_PAUSE)
1569          *         .compose()
1570          * </pre>
1571          */
1572         @FlaggedApi(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY)
1573         public static final int DELAY_TYPE_PAUSE = 0;
1574 
1575         /**
1576          * The delay represents an offset before starting this primitive, relative to the start
1577          * time of the previous primitive in the composition.
1578          *
1579          * <p>The primitive will start at the requested fixed time after the last primitive started,
1580          * independently of that primitive's actual duration on the device hardware. This enables
1581          * precise timings of primitives within a composition, ensuring they'll be played at the
1582          * desired intervals. Here is an example:
1583          *
1584          * <pre>
1585          *     VibrationEffect.startComposition()
1586          *         .addPrimitive(PRIMITIVE_CLICK, 1.0)
1587          *         .addPrimitive(PRIMITIVE_TICK, 1.0, 20, DELAY_TYPE_RELATIVE_START_OFFSET)
1588          *         .addPrimitive(PRIMITIVE_THUD, 1.0, 80, DELAY_TYPE_RELATIVE_START_OFFSET)
1589          *         .compose()
1590          * </pre>
1591          *
1592          * Will be performed on the device as follows:
1593          *
1594          * <pre>
1595          *  0ms               20ms                     100ms
1596          *  PRIMITIVE_CLICK---PRIMITIVE_TICK-----------PRIMITIVE_THUD
1597          * </pre>
1598          *
1599          * <p>A primitive will be dropped from the composition if it overlaps with previous ones.
1600          */
1601         @FlaggedApi(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY)
1602         public static final int DELAY_TYPE_RELATIVE_START_OFFSET = 1;
1603 
1604         private final ArrayList<VibrationEffectSegment> mSegments = new ArrayList<>();
1605         private int mRepeatIndex = -1;
1606 
1607         Composition() {}
1608 
1609         /**
1610          * Adds a time duration to the current composition, during which the vibrator will be
1611          * turned off.
1612          *
1613          * @param duration The length of time the vibrator should be off. Value must be non-negative
1614          *                 and will be truncated to milliseconds.
1615          * @return This {@link Composition} object to enable adding multiple elements in one chain.
1616          *
1617          * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
1618          * ending with a repeating effect.
1619          * @hide
1620          */
1621         @TestApi
1622         @NonNull
1623         public Composition addOffDuration(@NonNull Duration duration) {
1624             int durationMs = (int) duration.toMillis();
1625             Preconditions.checkArgumentNonnegative(durationMs, "Off period must be non-negative");
1626             if (durationMs > 0) {
1627                 // Created a segment sustaining the zero amplitude to represent the delay.
1628                 addSegment(new StepSegment(/* amplitude= */ 0, /* frequencyHz= */ 0,
1629                         (int) duration.toMillis()));
1630             }
1631             return this;
1632         }
1633 
1634         /**
1635          * Add a haptic effect to the end of the current composition.
1636          *
1637          * <p>If this effect is repeating (e.g. created by {@link VibrationEffect#createWaveform}
1638          * with a non-negative repeat index, or created by another composition that has effects
1639          * repeating indefinitely), then no more effects or primitives will be accepted by this
1640          * composition after this method. Such effects should be cancelled via
1641          * {@link Vibrator#cancel()}.
1642          *
1643          * @param effect The effect to add to the end of this composition.
1644          * @return This {@link Composition} object to enable adding multiple elements in one chain.
1645          *
1646          * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
1647          * ending with a repeating effect.
1648          * @hide
1649          */
1650         @TestApi
1651         @NonNull
1652         public Composition addEffect(@NonNull VibrationEffect effect) {
1653             return addSegments(effect);
1654         }
1655 
1656         /**
1657          * Add a haptic effect to the end of the current composition and play it on repeat,
1658          * indefinitely.
1659          *
1660          * <p>The entire effect will be played on repeat, indefinitely, after all other elements
1661          * already added to this composition are played. No more effects or primitives will be
1662          * accepted by this composition after this method. Such effects should be cancelled via
1663          * {@link Vibrator#cancel()}.
1664          *
1665          * @param effect The effect to add to the end of this composition, must be finite.
1666          * @return This {@link Composition} object to enable adding multiple elements in one chain,
1667          * although only {@link #compose()} can follow this call.
1668          *
1669          * @throws IllegalArgumentException if the given effect is already repeating indefinitely.
1670          * @throws UnreachableAfterRepeatingIndefinitelyException if the composition is currently
1671          * ending with a repeating effect.
1672          * @hide
1673          */
1674         @TestApi
1675         @NonNull
1676         public Composition repeatEffectIndefinitely(@NonNull VibrationEffect effect) {
1677             Preconditions.checkArgument(effect.getDuration() < Long.MAX_VALUE,
1678                     "Can't repeat an indefinitely repeating effect. Consider addEffect instead.");
1679             int previousSegmentCount = mSegments.size();
1680             addSegments(effect);
1681             // Set repeat after segments were added, since addSegments checks this index.
1682             mRepeatIndex = previousSegmentCount;
1683             return this;
1684         }
1685 
1686         /**
1687          * Add a haptic primitive to the end of the current composition.
1688          *
1689          * <p>Similar to {@link #addPrimitive(int, float, int)}, but with no delay and a
1690          * default scale applied.
1691          *
1692          * @param primitiveId The primitive to add
1693          * @return This {@link Composition} object to enable adding multiple elements in one chain.
1694          */
1695         @NonNull
1696         public Composition addPrimitive(@PrimitiveType int primitiveId) {
1697             return addPrimitive(primitiveId, PrimitiveSegment.DEFAULT_SCALE);
1698         }
1699 
1700         /**
1701          * Add a haptic primitive to the end of the current composition.
1702          *
1703          * <p>Similar to {@link #addPrimitive(int, float, int)}, but with no delay.
1704          *
1705          * @param primitiveId The primitive to add
1706          * @param scale The scale to apply to the intensity of the primitive.
1707          * @return This {@link Composition} object to enable adding multiple elements in one chain.
1708          */
1709         @NonNull
1710         public Composition addPrimitive(@PrimitiveType int primitiveId,
1711                 @FloatRange(from = 0f, to = 1f) float scale) {
1712             return addPrimitive(primitiveId, scale, PrimitiveSegment.DEFAULT_DELAY_MILLIS);
1713         }
1714 
1715         /**
1716          * Add a haptic primitive to the end of the current composition.
1717          *
1718          * <p>Similar to {@link #addPrimitive(int, float, int, int)}, but default
1719          * delay type applied is {@link #DELAY_TYPE_PAUSE}.
1720          *
1721          * @param primitiveId The primitive to add
1722          * @param scale The scale to apply to the intensity of the primitive.
1723          * @param delay The amount of time in milliseconds to wait between the end of the last
1724          *              primitive and the beginning of this one (i.e. a pause in the composition).
1725          * @return This {@link Composition} object to enable adding multiple elements in one chain.
1726          */
1727         @NonNull
1728         public Composition addPrimitive(@PrimitiveType int primitiveId,
1729                 @FloatRange(from = 0f, to = 1f) float scale, @IntRange(from = 0) int delay) {
1730             return addPrimitive(primitiveId, scale, delay, PrimitiveSegment.DEFAULT_DELAY_TYPE);
1731         }
1732 
1733         /**
1734          * Add a haptic primitive to the end of the current composition.
1735          *
1736          * @param primitiveId The primitive to add
1737          * @param scale The scale to apply to the intensity of the primitive.
1738          * @param delay The amount of time in milliseconds to wait before playing this primitive,
1739          *              as defined by the given {@code delayType}.
1740          * @param delayType The type of delay to be applied, e.g. a pause between last primitive and
1741          *                  this one or a start offset.
1742          * @return This {@link Composition} object to enable adding multiple elements in one chain.
1743          */
1744         @FlaggedApi(Flags.FLAG_PRIMITIVE_COMPOSITION_ABSOLUTE_DELAY)
1745         @NonNull
1746         public Composition addPrimitive(@PrimitiveType int primitiveId,
1747                 @FloatRange(from = 0f, to = 1f) float scale, @IntRange(from = 0) int delay,
1748                 @DelayType int delayType) {
1749             PrimitiveSegment primitive = new PrimitiveSegment(primitiveId, scale, delay, delayType);
1750             primitive.validate();
1751             return addSegment(primitive);
1752         }
1753 
1754         private Composition addSegment(VibrationEffectSegment segment) {
1755             if (mRepeatIndex >= 0) {
1756                 throw new UnreachableAfterRepeatingIndefinitelyException();
1757             }
1758             mSegments.add(segment);
1759             return this;
1760         }
1761 
1762         private Composition addSegments(VibrationEffect effect) {
1763             if (mRepeatIndex >= 0) {
1764                 throw new UnreachableAfterRepeatingIndefinitelyException();
1765             }
1766             if (!(effect instanceof Composed composed)) {
1767                 throw new IllegalArgumentException("Can't add vendor effects to composition.");
1768             }
1769             if (composed.getRepeatIndex() >= 0) {
1770                 // Start repeating from the index relative to the composed waveform.
1771                 mRepeatIndex = mSegments.size() + composed.getRepeatIndex();
1772             }
1773             mSegments.addAll(composed.getSegments());
1774             return this;
1775         }
1776 
1777         /**
1778          * Compose all of the added primitives together into a single {@link VibrationEffect}.
1779          *
1780          * <p>The {@link Composition} object is still valid after this call, so you can continue
1781          * adding more primitives to it and generating more {@link VibrationEffect}s by calling this
1782          * method again.
1783          *
1784          * @return The {@link VibrationEffect} resulting from the composition of the primitives.
1785          */
1786         @NonNull
1787         public VibrationEffect compose() {
1788             if (mSegments.isEmpty()) {
1789                 throw new IllegalStateException(
1790                         "Composition must have at least one element to compose.");
1791             }
1792             VibrationEffect effect = new Composed(mSegments, mRepeatIndex);
1793             effect.validate();
1794             return effect;
1795         }
1796 
1797         /**
1798          * Convert the primitive ID to a human readable string for debugging.
1799          * @param id The ID to convert
1800          * @return The ID in a human readable format.
1801          * @hide
1802          */
1803         public static String primitiveToString(@PrimitiveType int id) {
1804             return switch (id) {
1805                 case PRIMITIVE_NOOP -> "NOOP";
1806                 case PRIMITIVE_CLICK -> "CLICK";
1807                 case PRIMITIVE_THUD -> "THUD";
1808                 case PRIMITIVE_SPIN -> "SPIN";
1809                 case PRIMITIVE_QUICK_RISE -> "QUICK_RISE";
1810                 case PRIMITIVE_SLOW_RISE -> "SLOW_RISE";
1811                 case PRIMITIVE_QUICK_FALL -> "QUICK_FALL";
1812                 case PRIMITIVE_TICK -> "TICK";
1813                 case PRIMITIVE_LOW_TICK -> "LOW_TICK";
1814                 default -> Integer.toString(id);
1815             };
1816         }
1817 
1818         /**
1819          * Convert the delay type to a human readable string for debugging.
1820          * @param type The delay type to convert
1821          * @return The delay type in a human readable format.
1822          * @hide
1823          */
1824         public static String delayTypeToString(@DelayType int type) {
1825             return switch (type) {
1826                 case DELAY_TYPE_PAUSE -> "PAUSE";
1827                 case DELAY_TYPE_RELATIVE_START_OFFSET -> "START_OFFSET";
1828                 default -> Integer.toString(type);
1829             };
1830         }
1831     }
1832 
1833     /**
1834      * A builder for waveform effects described by its envelope.
1835      *
1836      * <p>Waveform effect envelopes are defined by one or more control points describing a target
1837      * vibration amplitude and frequency, and a duration to reach those targets. The vibrator
1838      * will perform smooth transitions between control points.
1839      *
1840      * <p>For example, the following code ramps a vibrator from off to full amplitude at 120Hz over
1841      * 100ms, holds that state for 200ms, and then ramps back down over 100ms:
1842      *
1843      * <pre>{@code
1844      * VibrationEffect effect = new VibrationEffect.WaveformEnvelopeBuilder()
1845      *     .addControlPoint(1.0f, 120f, 100)
1846      *     .addControlPoint(1.0f, 120f, 200)
1847      *     .addControlPoint(0.0f, 120f, 100)
1848      *     .build();
1849      * }</pre>
1850      *
1851      * <p>The builder automatically starts all effects at 0 amplitude.
1852      *
1853      * <p>It is crucial to ensure that the frequency range used in your effect is compatible with
1854      * the device's capabilities. The framework will not play any frequencies that fall partially
1855      * or completely outside the device's supported range. It will also not attempt to correct or
1856      * modify these frequencies.
1857      *
1858      * <p>Therefore, it is strongly recommended that you design your haptic effects with the
1859      * device's frequency profile in mind. You can obtain the supported frequency range and other
1860      * relevant frequency-related information by getting the
1861      * {@link android.os.vibrator.VibratorFrequencyProfile} using the
1862      * {@link Vibrator#getFrequencyProfile()} method.
1863      *
1864      * <p>In addition to these limitations, when designing vibration patterns, it is important to
1865      * consider the physical limitations of the vibration actuator. These limitations include
1866      * factors such as the maximum number of control points allowed in an envelope effect, the
1867      * minimum and maximum durations permitted for each control point, and the maximum overall
1868      * duration of the effect. If a pattern exceeds the maximum number of allowed control points,
1869      * the framework will automatically break down the effect to ensure it plays correctly.
1870      *
1871      * <p>You can use the following APIs to obtain these limits:
1872      * <ul>
1873      * <li>Maximum envelope control points: {@link VibratorEnvelopeEffectInfo#getMaxSize()}
1874      * <li>Minimum control point duration:
1875      * {@link VibratorEnvelopeEffectInfo#getMinControlPointDurationMillis()}
1876      * <li>Maximum control point duration:
1877      * {@link VibratorEnvelopeEffectInfo#getMaxControlPointDurationMillis()}
1878      * <li>Maximum total effect duration: {@link VibratorEnvelopeEffectInfo#getMaxDurationMillis()}
1879      * </ul>
1880      */
1881     @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
1882     public static final class WaveformEnvelopeBuilder {
1883 
1884         private ArrayList<PwleSegment> mSegments = new ArrayList<>();
1885         private float mLastAmplitude = 0f;
1886         private float mLastFrequencyHz = Float.NaN;
1887 
1888         public WaveformEnvelopeBuilder() {}
1889 
1890         /**
1891          * Sets the initial frequency for the waveform in Hertz.
1892          *
1893          * <p>The effect will start vibrating at this frequency when it transitions to the
1894          * amplitude and frequency defined by the first control point.
1895          *
1896          * <p>The frequency must be greater than zero and within the supported range. To determine
1897          * the supported range, use {@link Vibrator#getFrequencyProfile()}. Creating
1898          * effects using frequencies outside this range will result in the vibration not playing.
1899          *
1900          * @param initialFrequencyHz The starting frequency of the vibration, in Hz. Must be
1901          *                           greater than zero.
1902          */
1903         @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
1904         @SuppressWarnings("MissingGetterMatchingBuilder")// No getter to initial frequency once set.
1905         @NonNull
1906         public WaveformEnvelopeBuilder setInitialFrequencyHz(
1907                 @FloatRange(from = 0) float initialFrequencyHz) {
1908 
1909             if (mSegments.isEmpty()) {
1910                 mLastFrequencyHz = initialFrequencyHz;
1911             } else {
1912                 PwleSegment firstSegment = mSegments.getFirst();
1913                 mSegments.set(0, new PwleSegment(
1914                         firstSegment.getStartAmplitude(),
1915                         firstSegment.getEndAmplitude(),
1916                         initialFrequencyHz, // Update start frequency
1917                         firstSegment.getEndFrequencyHz(),
1918                         firstSegment.getDuration()));
1919             }
1920 
1921             return this;
1922         }
1923 
1924         /**
1925          * Adds a new control point to the end of this waveform envelope.
1926          *
1927          * <p>Amplitude defines the vibrator's strength at this frequency, ranging from 0 (off) to 1
1928          * (maximum achievable strength). This value scales linearly with output strength, not
1929          * perceived intensity. It's determined by the actuator response curve.
1930          *
1931          * <p>Frequency must be greater than zero and within the supported range. To determine
1932          * the supported range, use {@link Vibrator#getFrequencyProfile()}. Creating
1933          * effects using frequencies outside this range will result in the vibration not playing.
1934          *
1935          * <p>Time specifies the duration (in milliseconds) for the vibrator to smoothly transition
1936          * from the previous control point to this new one. It must be greater than zero. To
1937          * transition as quickly as possible, use
1938          * {@link VibratorEnvelopeEffectInfo#getMinControlPointDurationMillis()}.
1939          *
1940          * @param amplitude      The amplitude value between 0 and 1, inclusive. 0 represents the
1941          *                       vibrator being off, and 1 represents the maximum achievable
1942          *                       amplitude
1943          *                       at this frequency.
1944          * @param frequencyHz    The frequency in Hz, must be greater than zero.
1945          * @param durationMillis The transition time in milliseconds.
1946          */
1947         @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
1948         @SuppressWarnings("MissingGetterMatchingBuilder") // No getters to segments once created.
1949         @NonNull
1950         public WaveformEnvelopeBuilder addControlPoint(
1951                 @FloatRange(from = 0, to = 1) float amplitude,
1952                 @FloatRange(from = 0) float frequencyHz, @DurationMillisLong long durationMillis) {
1953 
1954             if (Float.isNaN(mLastFrequencyHz)) {
1955                 mLastFrequencyHz = frequencyHz;
1956             }
1957 
1958             mSegments.add(new PwleSegment(mLastAmplitude, amplitude, mLastFrequencyHz, frequencyHz,
1959                     durationMillis));
1960 
1961             mLastAmplitude = amplitude;
1962             mLastFrequencyHz = frequencyHz;
1963 
1964             return this;
1965         }
1966 
1967         /**
1968          * Build the waveform as a single {@link VibrationEffect}.
1969          *
1970          * <p>The {@link WaveformEnvelopeBuilder} object is still valid after this call, so you can
1971          * continue adding more primitives to it and generating more {@link VibrationEffect}s by
1972          * calling this method again.
1973          *
1974          * @return The {@link VibrationEffect} resulting from the list of control points.
1975          * @throws IllegalStateException if no control points were added to the builder.
1976          */
1977         @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
1978         @NonNull
1979         public VibrationEffect build() {
1980             if (mSegments.isEmpty()) {
1981                 throw new IllegalStateException(
1982                         "WaveformEnvelopeBuilder must have at least one control point to build.");
1983             }
1984             VibrationEffect effect = new Composed(mSegments, /* repeatIndex= */ -1);
1985             effect.validate();
1986             return effect;
1987         }
1988     }
1989 
1990     /**
1991      * A builder for waveform effects defined by their envelope, designed to provide a consistent
1992      * haptic perception across devices with varying capabilities.
1993      *
1994      * <p>This builder simplifies the creation of waveform effects by automatically adapting them
1995      * to different devices based on their capabilities. Effects are defined by control points
1996      * specifying target vibration intensity and sharpness, along with durations to reach those
1997      * targets. The vibrator will smoothly transition between these control points.
1998      *
1999      * <p><b>Intensity:</b> Defines the overall strength of the vibration, ranging from
2000      * 0 (off) to 1 (maximum achievable strength). Higher values result in stronger
2001      * vibrations. Supported intensity values guarantee sensitivity levels (SL) above
2002      * 10 dB SL to ensure human perception.
2003      *
2004      * <p><b>Sharpness:</b> Defines the crispness of the vibration, ranging from 0 to 1.
2005      * Lower values produce smoother vibrations, while higher values create a sharper,
2006      * more snappy sensation. Sharpness is mapped to its equivalent frequency within
2007      * the device's supported frequency range.
2008      *
2009      * <p>While this builder handles most of the adaptation logic, it does come with some
2010      * limitations:
2011      * <ul>
2012      *     <li>It may not use the full range of frequencies</li>
2013      *     <li>It's restricted to a frequency range that can generate output of at least 10 db
2014      *     SL</li>
2015      *     <li>Effects must end with a zero intensity control point. Failure to end at a zero
2016      *     intensity control point will result in an {@link IllegalStateException}.</li>
2017      * </ul>
2018      *
2019      * <p>The builder automatically starts all effects at 0 intensity.
2020      *
2021      * <p>To avoid these limitations and to have more control over the effects output, use
2022      * {@link WaveformEnvelopeBuilder}, where direct amplitude and frequency values can be used.
2023      *
2024      * <p>For optimal cross-device consistency, it's recommended to limit the number of control
2025      * points to a maximum of 16. However this is not mandatory, and if a pattern exceeds the
2026      * maximum number of allowed control points, the framework will automatically break down the
2027      * effect to ensure it plays correctly.
2028      *
2029      * <p>For example, the following code creates a vibration effect that ramps up the intensity
2030      * from a low-pitched to a high-pitched strong vibration over 500ms and then ramps it down to
2031      * 0 (off) over 100ms:
2032      *
2033      * <pre>{@code
2034      * VibrationEffect effect = new VibrationEffect.BasicEnvelopeBuilder()
2035      *     .setInitialSharpness(0.0f)
2036      *     .addControlPoint(1.0f, 1.0f, 500)
2037      *     .addControlPoint(0.0f, 1.0f, 100)
2038      *     .build();
2039      * }</pre>
2040      */
2041     @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
2042     public static final class BasicEnvelopeBuilder {
2043 
2044         private ArrayList<BasicPwleSegment> mSegments = new ArrayList<>();
2045         private float mLastIntensity = 0f;
2046         private float mLastSharpness = Float.NaN;
2047 
2048         public BasicEnvelopeBuilder() {}
2049 
2050         /**
2051          * Sets the initial sharpness for the basic envelope effect.
2052          *
2053          * <p>The effect will start vibrating at this sharpness when it transitions to the
2054          * intensity and sharpness defined by the first control point.
2055          *
2056          * <p> The sharpness defines the crispness of the vibration, ranging from 0 to 1. Lower
2057          * values translate to smoother vibrations, while higher values create a sharper more snappy
2058          * sensation. This value is mapped to the supported frequency range of the device.
2059          *
2060          * @param initialSharpness The starting sharpness of the vibration in the range of [0, 1].
2061          */
2062         @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
2063         @SuppressWarnings("MissingGetterMatchingBuilder")// No getter to initial sharpness once set.
2064         @NonNull
2065         public BasicEnvelopeBuilder setInitialSharpness(
2066                 @FloatRange(from = 0, to = 1) float initialSharpness) {
2067 
2068             if (mSegments.isEmpty()) {
2069                 mLastSharpness = initialSharpness;
2070             } else {
2071                 BasicPwleSegment firstSegment = mSegments.getFirst();
2072                 mSegments.set(0, new BasicPwleSegment(
2073                         firstSegment.getStartIntensity(),
2074                         firstSegment.getEndIntensity(),
2075                         initialSharpness, // Update start sharpness
2076                         firstSegment.getEndSharpness(),
2077                         firstSegment.getDuration()));
2078             }
2079 
2080             return this;
2081         }
2082 
2083         /**
2084          * Adds a new control point to the end of this waveform envelope.
2085          *
2086          * <p>Intensity defines the overall strength of the vibration, ranging from 0 (off) to 1
2087          * (maximum achievable strength). Higher values translate to stronger vibrations.
2088          *
2089          * <p>Sharpness defines the crispness of the vibration, ranging from 0 to 1. Lower
2090          * values translate to smoother vibrations, while higher values create a sharper more snappy
2091          * sensation. This value is mapped to the supported frequency range of the device.
2092          *
2093          * <p>Time specifies the duration (in milliseconds) for the vibrator to smoothly transition
2094          * from the previous control point to this new one. It must be greater than zero. To
2095          * transition as quickly as possible, use
2096          * {@link VibratorEnvelopeEffectInfo#getMinControlPointDurationMillis()}.
2097          *
2098          * @param intensity      The target vibration intensity, ranging from 0 (off) to 1 (maximum
2099          *                       strength).
2100          * @param sharpness      The target sharpness, ranging from 0 (smoothest) to 1 (sharpest).
2101          * @param durationMillis The transition time in milliseconds.
2102          */
2103         @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
2104         @SuppressWarnings("MissingGetterMatchingBuilder") // No getters to segments once created.
2105         @NonNull
2106         public BasicEnvelopeBuilder addControlPoint(
2107                 @FloatRange(from = 0, to = 1) float intensity,
2108                 @FloatRange(from = 0, to = 1) float sharpness,
2109                 @DurationMillisLong long durationMillis) {
2110 
2111             if (Float.isNaN(mLastSharpness)) {
2112                 mLastSharpness = sharpness;
2113             }
2114 
2115             mSegments.add(new BasicPwleSegment(mLastIntensity, intensity, mLastSharpness, sharpness,
2116                     durationMillis));
2117 
2118             mLastIntensity = intensity;
2119             mLastSharpness = sharpness;
2120 
2121             return this;
2122         }
2123 
2124         /**
2125          * Build the waveform as a single {@link VibrationEffect}.
2126          *
2127          * <p>The {@link BasicEnvelopeBuilder} object is still valid after this call, so you can
2128          * continue adding more primitives to it and generating more {@link VibrationEffect}s by
2129          * calling this method again.
2130          *
2131          * @return The {@link VibrationEffect} resulting from the list of control points.
2132          * @throws IllegalStateException if the last control point does not end at zero intensity.
2133          */
2134         @FlaggedApi(Flags.FLAG_NORMALIZED_PWLE_EFFECTS)
2135         @NonNull
2136         public VibrationEffect build() {
2137             if (mSegments.isEmpty()) {
2138                 throw new IllegalStateException(
2139                         "BasicEnvelopeBuilder must have at least one control point to build.");
2140             }
2141             if (mSegments.getLast().getEndIntensity() != 0) {
2142                 throw new IllegalStateException(
2143                         "Basic envelope effects must end at a zero intensity control point.");
2144             }
2145             VibrationEffect effect = new Composed(mSegments, /* repeatIndex= */ -1);
2146             effect.validate();
2147             return effect;
2148         }
2149 
2150     }
2151 
2152     /**
2153      * A builder for waveform haptic effects.
2154      *
2155      * <p>Waveform vibrations constitute of one or more timed transitions to new sets of vibration
2156      * parameters. These parameters can be the vibration amplitude, frequency, or both.
2157      *
2158      * <p>The following example ramps a vibrator turned off to full amplitude at 120Hz, over 100ms
2159      * starting at 60Hz, then holds that state for 200ms and ramps back down again over 100ms:
2160      *
2161      * <pre>
2162      * {@code import static android.os.VibrationEffect.VibrationParameter.targetAmplitude;
2163      * import static android.os.VibrationEffect.VibrationParameter.targetFrequency;
2164      *
2165      * VibrationEffect effect = VibrationEffect.startWaveform(targetFrequency(60))
2166      *     .addTransition(Duration.ofMillis(100), targetAmplitude(1), targetFrequency(120))
2167      *     .addSustain(Duration.ofMillis(200))
2168      *     .addTransition(Duration.ofMillis(100), targetAmplitude(0), targetFrequency(60))
2169      *     .build();}</pre>
2170      *
2171      * <p>The initial state of the waveform can be set via
2172      * {@link VibrationEffect#startWaveform(VibrationParameter)} or
2173      * {@link VibrationEffect#startWaveform(VibrationParameter, VibrationParameter)}. If the initial
2174      * parameters are not set then the {@link WaveformBuilder} will start with the vibrator off,
2175      * represented by zero amplitude, at the vibrator's resonant frequency.
2176      *
2177      * <p>Repeating waveforms can be created by building the repeating block separately and adding
2178      * it to the end of a composition with
2179      * {@link Composition#repeatEffectIndefinitely(VibrationEffect)}:
2180      *
2181      * <p>Note that physical vibration actuators have different reaction times for changing
2182      * amplitude and frequency. Durations specified here represent a timeline for the target
2183      * parameters, and quality of effects may be improved if the durations allow time for a
2184      * transition to be smoothly applied.
2185      *
2186      * <p>The following example illustrates both an initial state and a repeating section, using
2187      * a {@link VibrationEffect.Composition}. The resulting effect will have a tick followed by a
2188      * repeated beating effect with a rise that stretches out and a sharp finish.
2189      *
2190      * <pre>
2191      * {@code VibrationEffect patternToRepeat = VibrationEffect.startWaveform(targetAmplitude(0.2f))
2192      *     .addSustain(Duration.ofMillis(10))
2193      *     .addTransition(Duration.ofMillis(20), targetAmplitude(0.4f))
2194      *     .addSustain(Duration.ofMillis(30))
2195      *     .addTransition(Duration.ofMillis(40), targetAmplitude(0.8f))
2196      *     .addSustain(Duration.ofMillis(50))
2197      *     .addTransition(Duration.ofMillis(60), targetAmplitude(0.2f))
2198      *     .build();
2199      *
2200      * VibrationEffect effect = VibrationEffect.startComposition()
2201      *     .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK)
2202      *     .addOffDuration(Duration.ofMillis(20))
2203      *     .repeatEffectIndefinitely(patternToRepeat)
2204      *     .compose();}</pre>
2205      *
2206      * <p>The amplitude step waveforms that can be created via
2207      * {@link VibrationEffect#createWaveform(long[], int[], int)} can also be created with
2208      * {@link WaveformBuilder} by adding zero duration transitions:
2209      *
2210      * <pre>
2211      * {@code // These two effects are the same
2212      * VibrationEffect waveform = VibrationEffect.createWaveform(
2213      *     new long[] { 10, 20, 30 },  // timings in milliseconds
2214      *     new int[] { 51, 102, 204 }, // amplitudes in [0,255]
2215      *     -1);                        // repeat index
2216      *
2217      * VibrationEffect sameWaveform = VibrationEffect.startWaveform(targetAmplitude(0.2f))
2218      *     .addSustain(Duration.ofMillis(10))
2219      *     .addTransition(Duration.ZERO, targetAmplitude(0.4f))
2220      *     .addSustain(Duration.ofMillis(20))
2221      *     .addTransition(Duration.ZERO, targetAmplitude(0.8f))
2222      *     .addSustain(Duration.ofMillis(30))
2223      *     .build();}</pre>
2224      *
2225      * @see VibrationEffect#startWaveform
2226      * @hide
2227      */
2228     @TestApi
2229     public static final class WaveformBuilder {
2230         // Epsilon used for float comparison of amplitude and frequency values on transitions.
2231         private static final float EPSILON = 1e-5f;
2232 
2233         private ArrayList<VibrationEffectSegment> mSegments = new ArrayList<>();
2234         private float mLastAmplitude = 0f;
2235         private float mLastFrequencyHz = 0f;
2236 
2237         WaveformBuilder() {}
2238 
2239         /**
2240          * Add a transition to new vibration parameter value to the end of this waveform.
2241          *
2242          * <p>The duration represents how long the vibrator should take to smoothly transition to
2243          * the new vibration parameter. If the duration is zero then the vibrator will jump to the
2244          * new value as fast as possible.
2245          *
2246          * <p>Vibration parameter values will be truncated to conform to the device capabilities
2247          * according to the {@link VibratorFrequencyProfileLegacy}.
2248          *
2249          * @param duration        The length of time this transition should take. Value must be
2250          *                        non-negative and will be truncated to milliseconds.
2251          * @param targetParameter The new target {@link VibrationParameter} value to be reached
2252          *                        after the given duration.
2253          * @return This {@link WaveformBuilder} object to enable adding multiple transitions in
2254          * chain.
2255          * @hide
2256          */
2257         @TestApi
2258         @SuppressWarnings("MissingGetterMatchingBuilder") // No getters to segments once created.
2259         @NonNull
2260         public WaveformBuilder addTransition(@NonNull Duration duration,
2261                 @NonNull VibrationParameter targetParameter) {
2262             Preconditions.checkNotNull(duration, "Duration is null");
2263             checkVibrationParameter(targetParameter, "targetParameter");
2264             float amplitude = extractTargetAmplitude(targetParameter, /* target2= */ null);
2265             float frequencyHz = extractTargetFrequency(targetParameter, /* target2= */ null);
2266             addTransitionSegment(duration, amplitude, frequencyHz);
2267             return this;
2268         }
2269 
2270         /**
2271          * Add a transition to new vibration parameters to the end of this waveform.
2272          *
2273          * <p>The duration represents how long the vibrator should take to smoothly transition to
2274          * the new vibration parameters. If the duration is zero then the vibrator will jump to the
2275          * new values as fast as possible.
2276          *
2277          * <p>Vibration parameters values will be truncated to conform to the device capabilities
2278          * according to the {@link VibratorFrequencyProfileLegacy}.
2279          *
2280          * @param duration         The length of time this transition should take. Value must be
2281          *                         non-negative and will be truncated to milliseconds.
2282          * @param targetParameter1 The first target {@link VibrationParameter} value to be reached
2283          *                         after the given duration.
2284          * @param targetParameter2 The second target {@link VibrationParameter} value to be reached
2285          *                         after the given duration, must be a different type of parameter
2286          *                         than the one specified by the first argument.
2287          * @return This {@link WaveformBuilder} object to enable adding multiple transitions in
2288          * chain.
2289          * @hide
2290          */
2291         @TestApi
2292         @SuppressWarnings("MissingGetterMatchingBuilder") // No getters to segments once created.
2293         @NonNull
2294         public WaveformBuilder addTransition(@NonNull Duration duration,
2295                 @NonNull VibrationParameter targetParameter1,
2296                 @NonNull VibrationParameter targetParameter2) {
2297             Preconditions.checkNotNull(duration, "Duration is null");
2298             checkVibrationParameter(targetParameter1, "targetParameter1");
2299             checkVibrationParameter(targetParameter2, "targetParameter2");
2300             Preconditions.checkArgument(
2301                     !Objects.equals(targetParameter1.getClass(), targetParameter2.getClass()),
2302                     "Parameter arguments must specify different parameter types");
2303             float amplitude = extractTargetAmplitude(targetParameter1, targetParameter2);
2304             float frequencyHz = extractTargetFrequency(targetParameter1, targetParameter2);
2305             addTransitionSegment(duration, amplitude, frequencyHz);
2306             return this;
2307         }
2308 
2309         /**
2310          * Add a duration to sustain the last vibration parameters of this waveform.
2311          *
2312          * <p>The duration represents how long the vibrator should sustain the last set of
2313          * parameters provided to this builder.
2314          *
2315          * @param duration   The length of time the last values should be sustained by the vibrator.
2316          *                   Value must be >= 1ms.
2317          * @return This {@link WaveformBuilder} object to enable adding multiple transitions in
2318          * chain.
2319          * @hide
2320          */
2321         @TestApi
2322         @SuppressWarnings("MissingGetterMatchingBuilder") // No getters to segments once created.
2323         @NonNull
2324         public WaveformBuilder addSustain(@NonNull Duration duration) {
2325             int durationMs = (int) duration.toMillis();
2326             Preconditions.checkArgument(durationMs >= 1, "Sustain duration must be >= 1ms");
2327             mSegments.add(new StepSegment(mLastAmplitude, mLastFrequencyHz, durationMs));
2328             return this;
2329         }
2330 
2331         /**
2332          * Build the waveform as a single {@link VibrationEffect}.
2333          *
2334          * <p>The {@link WaveformBuilder} object is still valid after this call, so you can
2335          * continue adding more primitives to it and generating more {@link VibrationEffect}s by
2336          * calling this method again.
2337          *
2338          * @return The {@link VibrationEffect} resulting from the list of transitions.
2339          * @hide
2340          */
2341         @TestApi
2342         @NonNull
2343         public VibrationEffect build() {
2344             if (mSegments.isEmpty()) {
2345                 throw new IllegalStateException(
2346                         "WaveformBuilder must have at least one transition to build.");
2347             }
2348             VibrationEffect effect = new Composed(mSegments, /* repeatIndex= */ -1);
2349             effect.validate();
2350             return effect;
2351         }
2352 
2353         private void checkVibrationParameter(@NonNull VibrationParameter vibrationParameter,
2354                 String paramName) {
2355             Preconditions.checkNotNull(vibrationParameter, "%s is null", paramName);
2356             Preconditions.checkArgument(
2357                     (vibrationParameter instanceof AmplitudeVibrationParameter)
2358                             || (vibrationParameter instanceof FrequencyVibrationParameter),
2359                     "%s is a unknown parameter", paramName);
2360         }
2361 
2362         private float extractTargetAmplitude(@Nullable VibrationParameter target1,
2363                 @Nullable VibrationParameter target2) {
2364             if (target2 instanceof AmplitudeVibrationParameter) {
2365                 return ((AmplitudeVibrationParameter) target2).amplitude;
2366             }
2367             if (target1 instanceof AmplitudeVibrationParameter) {
2368                 return ((AmplitudeVibrationParameter) target1).amplitude;
2369             }
2370             return mLastAmplitude;
2371         }
2372 
2373         private float extractTargetFrequency(@Nullable VibrationParameter target1,
2374                 @Nullable VibrationParameter target2) {
2375             if (target2 instanceof FrequencyVibrationParameter) {
2376                 return ((FrequencyVibrationParameter) target2).frequencyHz;
2377             }
2378             if (target1 instanceof FrequencyVibrationParameter) {
2379                 return ((FrequencyVibrationParameter) target1).frequencyHz;
2380             }
2381             return mLastFrequencyHz;
2382         }
2383 
2384         private void addTransitionSegment(Duration duration, float targetAmplitude,
2385                 float targetFrequency) {
2386             Preconditions.checkNotNull(duration, "Duration is null");
2387             Preconditions.checkArgument(!duration.isNegative(),
2388                     "Transition duration must be non-negative");
2389             int durationMs = (int) duration.toMillis();
2390 
2391             // Ignore transitions with zero duration, but keep values for next additions.
2392             if (durationMs > 0) {
2393                 if ((Math.abs(mLastAmplitude - targetAmplitude) < EPSILON)
2394                         && (Math.abs(mLastFrequencyHz - targetFrequency) < EPSILON)) {
2395                     // No value is changing, this can be best represented by a step segment.
2396                     mSegments.add(new StepSegment(targetAmplitude, targetFrequency, durationMs));
2397                 } else {
2398                     mSegments.add(new RampSegment(mLastAmplitude, targetAmplitude,
2399                             mLastFrequencyHz, targetFrequency, durationMs));
2400                 }
2401             }
2402 
2403             mLastAmplitude = targetAmplitude;
2404             mLastFrequencyHz = targetFrequency;
2405         }
2406     }
2407 
2408     /**
2409      * A representation of a single vibration parameter.
2410      *
2411      * <p>This is to describe a waveform haptic effect, which consists of one or more timed
2412      * transitions to a new set of {@link VibrationParameter}s.
2413      *
2414      * <p>Examples of concrete parameters are the vibration amplitude or frequency.
2415      *
2416      * @see VibrationEffect.WaveformBuilder
2417      * @hide
2418      */
2419     @TestApi
2420     @SuppressWarnings("UserHandleName") // This is not a regular set of parameters, no *Params.
2421     public static class VibrationParameter {
2422         VibrationParameter() {
2423         }
2424 
2425         /**
2426          * The target vibration amplitude.
2427          *
2428          * @param amplitude The amplitude value, between 0 and 1, inclusive, where 0 represents the
2429          *                  vibrator turned off and 1 represents the maximum amplitude the vibrator
2430          *                  can reach across all supported frequencies.
2431          * @return The {@link VibrationParameter} instance that represents given amplitude.
2432          * @hide
2433          */
2434         @TestApi
2435         @NonNull
2436         public static VibrationParameter targetAmplitude(
2437                 @FloatRange(from = 0, to = 1) float amplitude) {
2438             return new AmplitudeVibrationParameter(amplitude);
2439         }
2440 
2441         /**
2442          * The target vibration frequency.
2443          *
2444          * @param frequencyHz The frequency value, in hertz.
2445          * @return The {@link VibrationParameter} instance that represents given frequency.
2446          * @hide
2447          */
2448         @TestApi
2449         @NonNull
2450         public static VibrationParameter targetFrequency(@FloatRange(from = 1) float frequencyHz) {
2451             return new FrequencyVibrationParameter(frequencyHz);
2452         }
2453     }
2454 
2455     /** The vibration amplitude, represented by a value in [0,1]. */
2456     private static final class AmplitudeVibrationParameter extends VibrationParameter {
2457         public final float amplitude;
2458 
2459         AmplitudeVibrationParameter(float amplitude) {
2460             Preconditions.checkArgument((amplitude >= 0) && (amplitude <= 1),
2461                     "Amplitude must be within [0,1]");
2462             this.amplitude = amplitude;
2463         }
2464     }
2465 
2466     /** The vibration frequency, in hertz, or zero to represent undefined frequency. */
2467     private static final class FrequencyVibrationParameter extends VibrationParameter {
2468         public final float frequencyHz;
2469 
2470         FrequencyVibrationParameter(float frequencyHz) {
2471             Preconditions.checkArgument(frequencyHz >= 1, "Frequency must be >= 1");
2472             Preconditions.checkArgument(Float.isFinite(frequencyHz), "Frequency must be finite");
2473             this.frequencyHz = frequencyHz;
2474         }
2475     }
2476 
2477     @NonNull
2478     public static final Parcelable.Creator<VibrationEffect> CREATOR =
2479             new Parcelable.Creator<VibrationEffect>() {
2480                 @Override
2481                 public VibrationEffect createFromParcel(Parcel in) {
2482                     switch (in.readInt()) {
2483                         case PARCEL_TOKEN_COMPOSED:
2484                             return new Composed(in);
2485                         case PARCEL_TOKEN_VENDOR_EFFECT:
2486                             if (Flags.vendorVibrationEffects()) {
2487                                 return new VendorEffect(in);
2488                             } // else fall through
2489                         default:
2490                             throw new IllegalStateException(
2491                                     "Unexpected vibration effect type token in parcel.");
2492                     }
2493                 }
2494                 @Override
2495                 public VibrationEffect[] newArray(int size) {
2496                     return new VibrationEffect[size];
2497                 }
2498             };
2499 }
2500