1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server.vibrator;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.hardware.vibrator.IVibratorManager;
22 import android.os.CombinedVibration;
23 import android.os.IBinder;
24 import android.os.PowerManager;
25 import android.os.Process;
26 import android.os.RemoteException;
27 import android.os.SystemClock;
28 import android.os.Trace;
29 import android.os.VibrationEffect;
30 import android.os.VibratorInfo;
31 import android.os.WorkSource;
32 import android.os.vibrator.PrebakedSegment;
33 import android.os.vibrator.PrimitiveSegment;
34 import android.os.vibrator.RampSegment;
35 import android.os.vibrator.StepSegment;
36 import android.os.vibrator.VibrationEffectSegment;
37 import android.util.Slog;
38 import android.util.SparseArray;
39 
40 import com.android.internal.annotations.GuardedBy;
41 import com.android.internal.annotations.VisibleForTesting;
42 import com.android.internal.app.IBatteryStats;
43 import com.android.internal.util.FrameworkStatsLog;
44 
45 import java.util.ArrayList;
46 import java.util.Arrays;
47 import java.util.Iterator;
48 import java.util.LinkedList;
49 import java.util.List;
50 import java.util.NoSuchElementException;
51 import java.util.PriorityQueue;
52 import java.util.Queue;
53 
54 /** Plays a {@link Vibration} in dedicated thread. */
55 final class VibrationThread extends Thread implements IBinder.DeathRecipient {
56     private static final String TAG = "VibrationThread";
57     private static final boolean DEBUG = false;
58 
59     /**
60      * Extra timeout added to the end of each vibration step to ensure it finishes even when
61      * vibrator callbacks are lost.
62      */
63     private static final long CALLBACKS_EXTRA_TIMEOUT = 1_000;
64 
65     /** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */
66     private static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
67 
68     /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */
69     private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000;
70 
71     private static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
72 
73     /** Callbacks for playing a {@link Vibration}. */
74     interface VibrationCallbacks {
75 
76         /**
77          * Callback triggered before starting a synchronized vibration step. This will be called
78          * with {@code requiredCapabilities = 0} if no synchronization is required.
79          *
80          * @param requiredCapabilities The required syncing capabilities for this preparation step.
81          *                             Expects a combination of values from
82          *                             IVibratorManager.CAP_PREPARE_* and
83          *                             IVibratorManager.CAP_MIXED_TRIGGER_*.
84          * @param vibratorIds          The id of the vibrators to be prepared.
85          */
prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds)86         boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds);
87 
88         /** Callback triggered after synchronized vibrations were prepared. */
triggerSyncedVibration(long vibrationId)89         boolean triggerSyncedVibration(long vibrationId);
90 
91         /** Callback triggered to cancel a prepared synced vibration. */
cancelSyncedVibration()92         void cancelSyncedVibration();
93 
94         /** Callback triggered when the vibration is complete. */
onVibrationCompleted(long vibrationId, Vibration.Status status)95         void onVibrationCompleted(long vibrationId, Vibration.Status status);
96 
97         /** Callback triggered when the vibrators are released after the thread is complete. */
onVibratorsReleased()98         void onVibratorsReleased();
99     }
100 
101     private final Object mLock = new Object();
102     private final WorkSource mWorkSource;
103     private final PowerManager.WakeLock mWakeLock;
104     private final IBatteryStats mBatteryStatsService;
105     private final VibrationSettings mVibrationSettings;
106     private final DeviceVibrationEffectAdapter mDeviceEffectAdapter;
107     private final Vibration mVibration;
108     private final VibrationCallbacks mCallbacks;
109     private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
110     private final StepQueue mStepQueue = new StepQueue();
111 
112     private volatile boolean mStop;
113     private volatile boolean mForceStop;
114     // Variable only set and read in main thread.
115     private boolean mCalledVibrationCompleteCallback = false;
116 
VibrationThread(Vibration vib, VibrationSettings vibrationSettings, DeviceVibrationEffectAdapter effectAdapter, SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService, VibrationCallbacks callbacks)117     VibrationThread(Vibration vib, VibrationSettings vibrationSettings,
118             DeviceVibrationEffectAdapter effectAdapter,
119             SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock,
120             IBatteryStats batteryStatsService, VibrationCallbacks callbacks) {
121         mVibration = vib;
122         mVibrationSettings = vibrationSettings;
123         mDeviceEffectAdapter = effectAdapter;
124         mCallbacks = callbacks;
125         mWorkSource = new WorkSource(mVibration.uid);
126         mWakeLock = wakeLock;
127         mBatteryStatsService = batteryStatsService;
128 
129         CombinedVibration effect = vib.getEffect();
130         for (int i = 0; i < availableVibrators.size(); i++) {
131             if (effect.hasVibrator(availableVibrators.keyAt(i))) {
132                 mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
133             }
134         }
135     }
136 
getVibration()137     Vibration getVibration() {
138         return mVibration;
139     }
140 
141     @VisibleForTesting
getVibrators()142     SparseArray<VibratorController> getVibrators() {
143         return mVibrators;
144     }
145 
146     @Override
binderDied()147     public void binderDied() {
148         if (DEBUG) {
149             Slog.d(TAG, "Binder died, cancelling vibration...");
150         }
151         cancel();
152     }
153 
154     @Override
run()155     public void run() {
156         // Structured to guarantee the vibrators completed and released callbacks at the end of
157         // thread execution. Both of these callbacks are exclusively called from this thread.
158         try {
159             try {
160                 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
161                 runWithWakeLock();
162             } finally {
163                 clientVibrationCompleteIfNotAlready(Vibration.Status.FINISHED_UNEXPECTED);
164             }
165         } finally {
166             mCallbacks.onVibratorsReleased();
167         }
168     }
169 
170     /** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
runWithWakeLock()171     private void runWithWakeLock() {
172         mWakeLock.setWorkSource(mWorkSource);
173         mWakeLock.acquire();
174         try {
175             runWithWakeLockAndDeathLink();
176         } finally {
177             mWakeLock.release();
178         }
179     }
180 
181     /**
182      * Runs the VibrationThread with the binder death link, handling link/unlink failures.
183      * Called from within runWithWakeLock.
184      */
runWithWakeLockAndDeathLink()185     private void runWithWakeLockAndDeathLink() {
186         try {
187             mVibration.token.linkToDeath(this, 0);
188         } catch (RemoteException e) {
189             Slog.e(TAG, "Error linking vibration to token death", e);
190             clientVibrationCompleteIfNotAlready(Vibration.Status.IGNORED_ERROR_TOKEN);
191             return;
192         }
193         // Ensure that the unlink always occurs now.
194         try {
195             // This is the actual execution of the vibration.
196             playVibration();
197         } finally {
198             try {
199                 mVibration.token.unlinkToDeath(this, 0);
200             } catch (NoSuchElementException e) {
201                 Slog.wtf(TAG, "Failed to unlink token", e);
202             }
203         }
204     }
205 
206     /** Cancel current vibration and ramp down the vibrators gracefully. */
cancel()207     public void cancel() {
208         if (mStop) {
209             // Already cancelled, running clean-up steps.
210             return;
211         }
212         mStop = true;
213         synchronized (mLock) {
214             if (DEBUG) {
215                 Slog.d(TAG, "Vibration cancelled");
216             }
217             mLock.notify();
218         }
219     }
220 
221     /** Cancel current vibration and shuts off the vibrators immediately. */
cancelImmediately()222     public void cancelImmediately() {
223         if (mForceStop) {
224             // Already forced the thread to stop, wait for it to finish.
225             return;
226         }
227         mStop = mForceStop = true;
228         synchronized (mLock) {
229             if (DEBUG) {
230                 Slog.d(TAG, "Vibration cancelled immediately");
231             }
232             mLock.notify();
233         }
234     }
235 
236     /** Notify current vibration that a synced step has completed. */
syncedVibrationComplete()237     public void syncedVibrationComplete() {
238         synchronized (mLock) {
239             if (DEBUG) {
240                 Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
241             }
242             for (int i = 0; i < mVibrators.size(); i++) {
243                 mStepQueue.notifyVibratorComplete(mVibrators.keyAt(i));
244             }
245             mLock.notify();
246         }
247     }
248 
249     /** Notify current vibration that a step has completed on given vibrator. */
vibratorComplete(int vibratorId)250     public void vibratorComplete(int vibratorId) {
251         synchronized (mLock) {
252             if (DEBUG) {
253                 Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
254             }
255             mStepQueue.notifyVibratorComplete(vibratorId);
256             mLock.notify();
257         }
258     }
259 
260     // Indicate that the vibration is complete. This can be called multiple times only for
261     // convenience of handling error conditions - an error after the client is complete won't
262     // affect the status.
clientVibrationCompleteIfNotAlready(Vibration.Status completedStatus)263     private void clientVibrationCompleteIfNotAlready(Vibration.Status completedStatus) {
264         if (!mCalledVibrationCompleteCallback) {
265             mCalledVibrationCompleteCallback = true;
266             mCallbacks.onVibrationCompleted(mVibration.id, completedStatus);
267         }
268     }
269 
playVibration()270     private void playVibration() {
271         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
272         try {
273             CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect());
274             final int sequentialEffectSize = sequentialEffect.getEffects().size();
275             mStepQueue.offer(new StartVibrateStep(sequentialEffect));
276 
277             while (!mStepQueue.isEmpty()) {
278                 long waitTime;
279                 synchronized (mLock) {
280                     waitTime = mStepQueue.calculateWaitTime();
281                     if (waitTime > 0) {
282                         try {
283                             mLock.wait(waitTime);
284                         } catch (InterruptedException e) {
285                         }
286                     }
287                 }
288                 // If we waited, the queue may have changed, so let the loop run again.
289                 if (waitTime <= 0) {
290                     mStepQueue.consumeNext();
291                 }
292                 Vibration.Status status = mStop ? Vibration.Status.CANCELLED
293                         : mStepQueue.calculateVibrationStatus(sequentialEffectSize);
294                 if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) {
295                     // First time vibration stopped running, start clean-up tasks and notify
296                     // callback immediately.
297                     clientVibrationCompleteIfNotAlready(status);
298                     if (status == Vibration.Status.CANCELLED) {
299                         mStepQueue.cancel();
300                     }
301                 }
302                 if (mForceStop) {
303                     // Cancel every step and stop playing them right away, even clean-up steps.
304                     mStepQueue.cancelImmediately();
305                     clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED);
306                     break;
307                 }
308             }
309         } finally {
310             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
311         }
312     }
313 
noteVibratorOn(long duration)314     private void noteVibratorOn(long duration) {
315         try {
316             if (duration <= 0) {
317                 return;
318             }
319             if (duration == Long.MAX_VALUE) {
320                 // Repeating duration has started. Report a fixed duration here, noteVibratorOff
321                 // should be called when this is cancelled.
322                 duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION;
323             }
324             mBatteryStatsService.noteVibratorOn(mVibration.uid, duration);
325             FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
326                     mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON,
327                     duration);
328         } catch (RemoteException e) {
329         }
330     }
331 
noteVibratorOff()332     private void noteVibratorOff() {
333         try {
334             mBatteryStatsService.noteVibratorOff(mVibration.uid);
335             FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED,
336                     mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF,
337                     /* duration= */ 0);
338         } catch (RemoteException e) {
339         }
340     }
341 
342     @Nullable
nextVibrateStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int segmentIndex, long vibratorOffTimeout)343     private SingleVibratorStep nextVibrateStep(long startTime, VibratorController controller,
344             VibrationEffect.Composed effect, int segmentIndex, long vibratorOffTimeout) {
345         if (segmentIndex >= effect.getSegments().size()) {
346             segmentIndex = effect.getRepeatIndex();
347         }
348         if (segmentIndex < 0) {
349             // No more segments to play, last step is to complete the vibration on this vibrator.
350             return new CompleteStep(startTime, /* cancelled= */ false, controller,
351                     vibratorOffTimeout);
352         }
353 
354         VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
355         if (segment instanceof PrebakedSegment) {
356             return new PerformStep(startTime, controller, effect, segmentIndex, vibratorOffTimeout);
357         }
358         if (segment instanceof PrimitiveSegment) {
359             return new ComposePrimitivesStep(startTime, controller, effect, segmentIndex,
360                     vibratorOffTimeout);
361         }
362         if (segment instanceof RampSegment) {
363             return new ComposePwleStep(startTime, controller, effect, segmentIndex,
364                     vibratorOffTimeout);
365         }
366         return new AmplitudeStep(startTime, controller, effect, segmentIndex, vibratorOffTimeout);
367     }
368 
toSequential(CombinedVibration effect)369     private static CombinedVibration.Sequential toSequential(CombinedVibration effect) {
370         if (effect instanceof CombinedVibration.Sequential) {
371             return (CombinedVibration.Sequential) effect;
372         }
373         return (CombinedVibration.Sequential) CombinedVibration.startSequential()
374                 .addNext(effect)
375                 .combine();
376     }
377 
378     /** Queue for {@link Step Steps}, sorted by their start time. */
379     private final class StepQueue {
380         @GuardedBy("mLock")
381         private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
382         @GuardedBy("mLock")
383         private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
384         @GuardedBy("mLock")
385         private final Queue<Integer> mNotifiedVibrators = new LinkedList<>();
386 
387         @GuardedBy("mLock")
388         private int mPendingVibrateSteps;
389         @GuardedBy("mLock")
390         private int mConsumedStartVibrateSteps;
391         @GuardedBy("mLock")
392         private int mSuccessfulVibratorOnSteps;
393         @GuardedBy("mLock")
394         private boolean mWaitToProcessVibratorCallbacks;
395 
offer(@onNull Step step)396         public void offer(@NonNull Step step) {
397             synchronized (mLock) {
398                 if (!step.isCleanUp()) {
399                     mPendingVibrateSteps++;
400                 }
401                 mNextSteps.offer(step);
402             }
403         }
404 
isEmpty()405         public boolean isEmpty() {
406             synchronized (mLock) {
407                 return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
408             }
409         }
410 
411         /**
412          * Calculate the {@link Vibration.Status} based on the current queue state and the expected
413          * number of {@link StartVibrateStep} to be played.
414          */
calculateVibrationStatus(int expectedStartVibrateSteps)415         public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) {
416             synchronized (mLock) {
417                 if (mPendingVibrateSteps > 0
418                         || mConsumedStartVibrateSteps < expectedStartVibrateSteps) {
419                     return Vibration.Status.RUNNING;
420                 }
421                 if (mSuccessfulVibratorOnSteps > 0) {
422                     return Vibration.Status.FINISHED;
423                 }
424                 // If no step was able to turn the vibrator ON successfully.
425                 return Vibration.Status.IGNORED_UNSUPPORTED;
426             }
427         }
428 
429         /** Returns the time in millis to wait before calling {@link #consumeNext()}. */
430         @GuardedBy("mLock")
calculateWaitTime()431         public long calculateWaitTime() {
432             if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
433                 // Steps anticipated by vibrator complete callback should be played right away.
434                 return 0;
435             }
436             Step nextStep = mNextSteps.peek();
437             return nextStep == null ? 0 : nextStep.calculateWaitTime();
438         }
439 
440         /**
441          * Play and remove the step at the top of this queue, and also adds the next steps generated
442          * to be played next.
443          */
consumeNext()444         public void consumeNext() {
445             // Vibrator callbacks should wait until the polled step is played and the next steps are
446             // added back to the queue, so they can handle the callback.
447             markWaitToProcessVibratorCallbacks();
448             try {
449                 Step nextStep = pollNext();
450                 if (nextStep != null) {
451                     // This might turn on the vibrator and have a HAL latency. Execute this outside
452                     // any lock to avoid blocking other interactions with the thread.
453                     List<Step> nextSteps = nextStep.play();
454                     synchronized (mLock) {
455                         if (nextStep.getVibratorOnDuration() > 0) {
456                             mSuccessfulVibratorOnSteps++;
457                         }
458                         if (nextStep instanceof StartVibrateStep) {
459                             mConsumedStartVibrateSteps++;
460                         }
461                         if (!nextStep.isCleanUp()) {
462                             mPendingVibrateSteps--;
463                         }
464                         for (int i = 0; i < nextSteps.size(); i++) {
465                             mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1;
466                         }
467                         mNextSteps.addAll(nextSteps);
468                     }
469                 }
470             } finally {
471                 synchronized (mLock) {
472                     processVibratorCallbacks();
473                 }
474             }
475         }
476 
477         /**
478          * Notify the vibrator completion.
479          *
480          * <p>This is a lightweight method that do not trigger any operation from {@link
481          * VibratorController}, so it can be called directly from a native callback.
482          */
483         @GuardedBy("mLock")
notifyVibratorComplete(int vibratorId)484         public void notifyVibratorComplete(int vibratorId) {
485             mNotifiedVibrators.offer(vibratorId);
486             if (!mWaitToProcessVibratorCallbacks) {
487                 // No step is being played or cancelled now, process the callback right away.
488                 processVibratorCallbacks();
489             }
490         }
491 
492         /**
493          * Cancel the current queue, replacing all remaining steps with respective clean-up steps.
494          *
495          * <p>This will remove all steps and replace them with respective
496          * {@link Step#cancel()}.
497          */
cancel()498         public void cancel() {
499             // Vibrator callbacks should wait until all steps from the queue are properly cancelled
500             // and clean up steps are added back to the queue, so they can handle the callback.
501             markWaitToProcessVibratorCallbacks();
502             try {
503                 List<Step> cleanUpSteps = new ArrayList<>();
504                 Step step;
505                 while ((step = pollNext()) != null) {
506                     cleanUpSteps.addAll(step.cancel());
507                 }
508                 synchronized (mLock) {
509                     // All steps generated by Step.cancel() should be clean-up steps.
510                     mPendingVibrateSteps = 0;
511                     mNextSteps.addAll(cleanUpSteps);
512                 }
513             } finally {
514                 synchronized (mLock) {
515                     processVibratorCallbacks();
516                 }
517             }
518         }
519 
520         /**
521          * Cancel the current queue immediately, clearing all remaining steps and skipping clean-up.
522          *
523          * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
524          */
cancelImmediately()525         public void cancelImmediately() {
526             // Vibrator callbacks should wait until all steps from the queue are properly cancelled.
527             markWaitToProcessVibratorCallbacks();
528             try {
529                 Step step;
530                 while ((step = pollNext()) != null) {
531                     // This might turn off the vibrator and have a HAL latency. Execute this outside
532                     // any lock to avoid blocking other interactions with the thread.
533                     step.cancelImmediately();
534                 }
535                 synchronized (mLock) {
536                     mPendingVibrateSteps = 0;
537                 }
538             } finally {
539                 synchronized (mLock) {
540                     processVibratorCallbacks();
541                 }
542             }
543         }
544 
545         @Nullable
pollNext()546         private Step pollNext() {
547             synchronized (mLock) {
548                 // Prioritize the steps anticipated by a vibrator complete callback.
549                 if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
550                     return mPendingOnVibratorCompleteSteps.poll();
551                 }
552                 return mNextSteps.poll();
553             }
554         }
555 
markWaitToProcessVibratorCallbacks()556         private void markWaitToProcessVibratorCallbacks() {
557             synchronized (mLock) {
558                 mWaitToProcessVibratorCallbacks = true;
559             }
560         }
561 
562         /**
563          * Notify the step in this queue that should be anticipated by the vibrator completion
564          * callback and keep it separate to be consumed by {@link #consumeNext()}.
565          *
566          * <p>This is a lightweight method that do not trigger any operation from {@link
567          * VibratorController}, so it can be called directly from a native callback.
568          *
569          * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
570          * first step found will be anticipated by this method, in no particular order.
571          */
572         @GuardedBy("mLock")
processVibratorCallbacks()573         private void processVibratorCallbacks() {
574             mWaitToProcessVibratorCallbacks = false;
575             while (!mNotifiedVibrators.isEmpty()) {
576                 int vibratorId = mNotifiedVibrators.poll();
577                 Iterator<Step> it = mNextSteps.iterator();
578                 while (it.hasNext()) {
579                     Step step = it.next();
580                     if (step.shouldPlayWhenVibratorComplete(vibratorId)) {
581                         it.remove();
582                         mPendingOnVibratorCompleteSteps.offer(step);
583                         break;
584                     }
585                 }
586             }
587         }
588     }
589 
590     /**
591      * Represent a single step for playing a vibration.
592      *
593      * <p>Every step has a start time, which can be used to apply delays between steps while
594      * executing them in sequence.
595      */
596     private abstract class Step implements Comparable<Step> {
597         public final long startTime;
598 
Step(long startTime)599         Step(long startTime) {
600             this.startTime = startTime;
601         }
602 
603         /**
604          * Returns true if this step is a clean up step and not part of a {@link VibrationEffect} or
605          * {@link CombinedVibration}.
606          */
isCleanUp()607         public boolean isCleanUp() {
608             return false;
609         }
610 
611         /** Play this step, returning a (possibly empty) list of next steps. */
612         @NonNull
play()613         public abstract List<Step> play();
614 
615         /**
616          * Cancel this pending step and return a (possibly empty) list of clean-up steps that should
617          * be played to gracefully cancel this step.
618          */
619         @NonNull
cancel()620         public abstract List<Step> cancel();
621 
622         /** Cancel this pending step immediately, skipping any clean-up. */
cancelImmediately()623         public abstract void cancelImmediately();
624 
625         /**
626          * Return the duration the vibrator was turned on when this step was played.
627          *
628          * @return A positive duration that the vibrator was turned on for by this step;
629          * Zero if the segment is not supported, the step was not played yet or vibrator was never
630          * turned on by this step; A negative value if the vibrator call has failed.
631          */
getVibratorOnDuration()632         public long getVibratorOnDuration() {
633             return 0;
634         }
635 
636         /**
637          * Return true to play this step right after a vibrator has notified vibration completed,
638          * used to anticipate steps waiting on vibrator callbacks with a timeout.
639          */
shouldPlayWhenVibratorComplete(int vibratorId)640         public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
641             return false;
642         }
643 
644         /**
645          * Returns the time in millis to wait before playing this step. This is performed
646          * while holding the queue lock, so should not rely on potentially slow operations.
647          */
calculateWaitTime()648         public long calculateWaitTime() {
649             if (startTime == Long.MAX_VALUE) {
650                 // This step don't have a predefined start time, it's just marked to be executed
651                 // after all other steps have finished.
652                 return 0;
653             }
654             return Math.max(0, startTime - SystemClock.uptimeMillis());
655         }
656 
657         @Override
compareTo(Step o)658         public int compareTo(Step o) {
659             return Long.compare(startTime, o.startTime);
660         }
661     }
662 
663     /**
664      * Starts a sync vibration.
665      *
666      * <p>If this step has successfully started playing a vibration on any vibrator, it will always
667      * add a {@link FinishVibrateStep} to the queue, to be played after all vibrators have finished
668      * all their individual steps.
669      *
670      * <o>If this step does not start any vibrator, it will add a {@link StartVibrateStep} if the
671      * sequential effect isn't finished yet.
672      */
673     private final class StartVibrateStep extends Step {
674         public final CombinedVibration.Sequential sequentialEffect;
675         public final int currentIndex;
676 
677         private long mVibratorsOnMaxDuration;
678 
StartVibrateStep(CombinedVibration.Sequential effect)679         StartVibrateStep(CombinedVibration.Sequential effect) {
680             this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0);
681         }
682 
StartVibrateStep(long startTime, CombinedVibration.Sequential effect, int index)683         StartVibrateStep(long startTime, CombinedVibration.Sequential effect, int index) {
684             super(startTime);
685             sequentialEffect = effect;
686             currentIndex = index;
687         }
688 
689         @Override
getVibratorOnDuration()690         public long getVibratorOnDuration() {
691             return mVibratorsOnMaxDuration;
692         }
693 
694         @Override
play()695         public List<Step> play() {
696             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep");
697             List<Step> nextSteps = new ArrayList<>();
698             mVibratorsOnMaxDuration = -1;
699             try {
700                 if (DEBUG) {
701                     Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex);
702                 }
703                 CombinedVibration effect = sequentialEffect.getEffects().get(currentIndex);
704                 DeviceEffectMap effectMapping = createEffectToVibratorMapping(effect);
705                 if (effectMapping == null) {
706                     // Unable to map effects to vibrators, ignore this step.
707                     return nextSteps;
708                 }
709 
710                 mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps);
711                 noteVibratorOn(mVibratorsOnMaxDuration);
712             } finally {
713                 if (mVibratorsOnMaxDuration >= 0) {
714                     // It least one vibrator was started then add a finish step to wait for all
715                     // active vibrators to finish their individual steps before going to the next.
716                     // Otherwise this step was ignored so just go to the next one.
717                     Step nextStep =
718                             mVibratorsOnMaxDuration > 0 ? new FinishVibrateStep(this) : nextStep();
719                     if (nextStep != null) {
720                         nextSteps.add(nextStep);
721                     }
722                 }
723                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
724             }
725             return nextSteps;
726         }
727 
728         @Override
cancel()729         public List<Step> cancel() {
730             return EMPTY_STEP_LIST;
731         }
732 
733         @Override
cancelImmediately()734         public void cancelImmediately() {
735         }
736 
737         /**
738          * Create the next {@link StartVibrateStep} to play this sequential effect, starting at the
739          * time this method is called, or null if sequence is complete.
740          */
741         @Nullable
nextStep()742         private Step nextStep() {
743             int nextIndex = currentIndex + 1;
744             if (nextIndex >= sequentialEffect.getEffects().size()) {
745                 return null;
746             }
747             long nextEffectDelay = sequentialEffect.getDelays().get(nextIndex);
748             long nextStartTime = SystemClock.uptimeMillis() + nextEffectDelay;
749             return new StartVibrateStep(nextStartTime, sequentialEffect, nextIndex);
750         }
751 
752         /** Create a mapping of individual {@link VibrationEffect} to available vibrators. */
753         @Nullable
createEffectToVibratorMapping( CombinedVibration effect)754         private DeviceEffectMap createEffectToVibratorMapping(
755                 CombinedVibration effect) {
756             if (effect instanceof CombinedVibration.Mono) {
757                 return new DeviceEffectMap((CombinedVibration.Mono) effect);
758             }
759             if (effect instanceof CombinedVibration.Stereo) {
760                 return new DeviceEffectMap((CombinedVibration.Stereo) effect);
761             }
762             return null;
763         }
764 
765         /**
766          * Starts playing effects on designated vibrators, in sync.
767          *
768          * @param effectMapping The {@link CombinedVibration} mapped to this device vibrators
769          * @param nextSteps     An output list to accumulate the future {@link Step Steps} created
770          *                      by this method, typically one for each vibrator that has
771          *                      successfully started vibrating on this step.
772          * @return The duration, in millis, of the {@link CombinedVibration}. Repeating
773          * waveforms return {@link Long#MAX_VALUE}. Zero or negative values indicate the vibrators
774          * have ignored all effects.
775          */
startVibrating(DeviceEffectMap effectMapping, List<Step> nextSteps)776         private long startVibrating(DeviceEffectMap effectMapping, List<Step> nextSteps) {
777             int vibratorCount = effectMapping.size();
778             if (vibratorCount == 0) {
779                 // No effect was mapped to any available vibrator.
780                 return 0;
781             }
782 
783             SingleVibratorStep[] steps = new SingleVibratorStep[vibratorCount];
784             long vibrationStartTime = SystemClock.uptimeMillis();
785             for (int i = 0; i < vibratorCount; i++) {
786                 steps[i] = nextVibrateStep(vibrationStartTime,
787                         mVibrators.get(effectMapping.vibratorIdAt(i)),
788                         effectMapping.effectAt(i),
789                         /* segmentIndex= */ 0, /* vibratorOffTimeout= */ 0);
790             }
791 
792             if (steps.length == 1) {
793                 // No need to prepare and trigger sync effects on a single vibrator.
794                 return startVibrating(steps[0], nextSteps);
795             }
796 
797             // This synchronization of vibrators should be executed one at a time, even if we are
798             // vibrating different sets of vibrators in parallel. The manager can only prepareSynced
799             // one set of vibrators at a time.
800             synchronized (mLock) {
801                 boolean hasPrepared = false;
802                 boolean hasTriggered = false;
803                 long maxDuration = 0;
804                 try {
805                     hasPrepared = mCallbacks.prepareSyncedVibration(
806                             effectMapping.getRequiredSyncCapabilities(),
807                             effectMapping.getVibratorIds());
808 
809                     for (SingleVibratorStep step : steps) {
810                         long duration = startVibrating(step, nextSteps);
811                         if (duration < 0) {
812                             // One vibrator has failed, fail this entire sync attempt.
813                             return maxDuration = -1;
814                         }
815                         maxDuration = Math.max(maxDuration, duration);
816                     }
817 
818                     // Check if sync was prepared and if any step was accepted by a vibrator,
819                     // otherwise there is nothing to trigger here.
820                     if (hasPrepared && maxDuration > 0) {
821                         hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id);
822                     }
823                     return maxDuration;
824                 } finally {
825                     if (hasPrepared && !hasTriggered) {
826                         // Trigger has failed or all steps were ignored by the vibrators.
827                         mCallbacks.cancelSyncedVibration();
828                         nextSteps.clear();
829                     } else if (maxDuration < 0) {
830                         // Some vibrator failed without being prepared so other vibrators might be
831                         // active. Cancel and remove every pending step from output list.
832                         for (int i = nextSteps.size() - 1; i >= 0; i--) {
833                             nextSteps.remove(i).cancelImmediately();
834                         }
835                     }
836                 }
837             }
838         }
839 
startVibrating(SingleVibratorStep step, List<Step> nextSteps)840         private long startVibrating(SingleVibratorStep step, List<Step> nextSteps) {
841             nextSteps.addAll(step.play());
842             long stepDuration = step.getVibratorOnDuration();
843             if (stepDuration < 0) {
844                 // Step failed, so return negative duration to propagate failure.
845                 return stepDuration;
846             }
847             // Return the longest estimation for the entire effect.
848             return Math.max(stepDuration, step.effect.getDuration());
849         }
850     }
851 
852     /**
853      * Finish a sync vibration started by a {@link StartVibrateStep}.
854      *
855      * <p>This only plays after all active vibrators steps have finished, and adds a {@link
856      * StartVibrateStep} to the queue if the sequential effect isn't finished yet.
857      */
858     private final class FinishVibrateStep extends Step {
859         public final StartVibrateStep startedStep;
860 
FinishVibrateStep(StartVibrateStep startedStep)861         FinishVibrateStep(StartVibrateStep startedStep) {
862             super(Long.MAX_VALUE); // No predefined startTime, just wait for all steps in the queue.
863             this.startedStep = startedStep;
864         }
865 
866         @Override
isCleanUp()867         public boolean isCleanUp() {
868             // This step only notes that all the vibrators has been turned off.
869             return true;
870         }
871 
872         @Override
play()873         public List<Step> play() {
874             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep");
875             try {
876                 if (DEBUG) {
877                     Slog.d(TAG, "FinishVibrateStep for effect #" + startedStep.currentIndex);
878                 }
879                 noteVibratorOff();
880                 Step nextStep = startedStep.nextStep();
881                 return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
882             } finally {
883                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
884             }
885         }
886 
887         @Override
cancel()888         public List<Step> cancel() {
889             cancelImmediately();
890             return EMPTY_STEP_LIST;
891         }
892 
893         @Override
cancelImmediately()894         public void cancelImmediately() {
895             noteVibratorOff();
896         }
897     }
898 
899     /**
900      * Represent a step on a single vibrator that plays one or more segments from a
901      * {@link VibrationEffect.Composed} effect.
902      */
903     private abstract class SingleVibratorStep extends Step {
904         public final VibratorController controller;
905         public final VibrationEffect.Composed effect;
906         public final int segmentIndex;
907         public final long vibratorOffTimeout;
908 
909         long mVibratorOnResult;
910         boolean mVibratorCallbackReceived;
911 
912         /**
913          * @param startTime          The time to schedule this step in the {@link StepQueue}.
914          * @param controller         The vibrator that is playing the effect.
915          * @param effect             The effect being played in this step.
916          * @param index              The index of the next segment to be played by this step
917          * @param vibratorOffTimeout The time the vibrator is expected to complete any previous
918          *                           vibration and turn off. This is used to allow this step to be
919          *                           anticipated when the completion callback is triggered, and can
920          *                           be used play effects back-to-back.
921          */
SingleVibratorStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)922         SingleVibratorStep(long startTime, VibratorController controller,
923                 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
924             super(startTime);
925             this.controller = controller;
926             this.effect = effect;
927             this.segmentIndex = index;
928             this.vibratorOffTimeout = vibratorOffTimeout;
929         }
930 
931         @Override
getVibratorOnDuration()932         public long getVibratorOnDuration() {
933             return mVibratorOnResult;
934         }
935 
936         @Override
shouldPlayWhenVibratorComplete(int vibratorId)937         public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
938             boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId;
939             mVibratorCallbackReceived |= isSameVibrator;
940             // Only anticipate this step if a timeout was set to wait for the vibration to complete,
941             // otherwise we are waiting for the correct time to play the next step.
942             return isSameVibrator && (vibratorOffTimeout > SystemClock.uptimeMillis());
943         }
944 
945         @Override
cancel()946         public List<Step> cancel() {
947             return Arrays.asList(new CompleteStep(SystemClock.uptimeMillis(),
948                     /* cancelled= */ true, controller, vibratorOffTimeout));
949         }
950 
951         @Override
cancelImmediately()952         public void cancelImmediately() {
953             if (vibratorOffTimeout > SystemClock.uptimeMillis()) {
954                 // Vibrator might be running from previous steps, so turn it off while canceling.
955                 stopVibrating();
956             }
957         }
958 
stopVibrating()959         void stopVibrating() {
960             if (DEBUG) {
961                 Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId());
962             }
963             controller.off();
964         }
965 
changeAmplitude(float amplitude)966         void changeAmplitude(float amplitude) {
967             if (DEBUG) {
968                 Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId()
969                         + " to " + amplitude);
970             }
971             controller.setAmplitude(amplitude);
972         }
973 
974         /** Return the {@link #nextVibrateStep} with same timings, only jumping the segments. */
skipToNextSteps(int segmentsSkipped)975         public List<Step> skipToNextSteps(int segmentsSkipped) {
976             return nextSteps(startTime, vibratorOffTimeout, segmentsSkipped);
977         }
978 
979         /**
980          * Return the {@link #nextVibrateStep} with same start and off timings calculated from
981          * {@link #getVibratorOnDuration()}, jumping all played segments.
982          *
983          * <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator
984          * result is non-positive, meaning the vibrator has either ignored or failed to turn on.
985          */
nextSteps(int segmentsPlayed)986         public List<Step> nextSteps(int segmentsPlayed) {
987             if (mVibratorOnResult <= 0) {
988                 // Vibration was not started, so just skip the played segments and keep timings.
989                 return skipToNextSteps(segmentsPlayed);
990             }
991             long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult;
992             long nextVibratorOffTimeout = nextStartTime + CALLBACKS_EXTRA_TIMEOUT;
993             return nextSteps(nextStartTime, nextVibratorOffTimeout, segmentsPlayed);
994         }
995 
996         /**
997          * Return the {@link #nextVibrateStep} with given start and off timings, which might be
998          * calculated independently, jumping all played segments.
999          *
1000          * <p>This should be used when the vibrator on/off state is not responsible for the steps
1001          * execution timings, e.g. while playing the vibrator amplitudes.
1002          */
nextSteps(long nextStartTime, long vibratorOffTimeout, int segmentsPlayed)1003         public List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout,
1004                 int segmentsPlayed) {
1005             Step nextStep = nextVibrateStep(nextStartTime, controller, effect,
1006                     segmentIndex + segmentsPlayed, vibratorOffTimeout);
1007             return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep);
1008         }
1009     }
1010 
1011     /**
1012      * Represent a step turn the vibrator on with a single prebaked effect.
1013      *
1014      * <p>This step automatically falls back by replacing the prebaked segment with
1015      * {@link VibrationSettings#getFallbackEffect(int)}, if available.
1016      */
1017     private final class PerformStep extends SingleVibratorStep {
1018 
PerformStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)1019         PerformStep(long startTime, VibratorController controller,
1020                 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
1021             // This step should wait for the last vibration to finish (with the timeout) and for the
1022             // intended step start time (to respect the effect delays).
1023             super(Math.max(startTime, vibratorOffTimeout), controller, effect, index,
1024                     vibratorOffTimeout);
1025         }
1026 
1027         @Override
play()1028         public List<Step> play() {
1029             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformStep");
1030             try {
1031                 VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
1032                 if (!(segment instanceof PrebakedSegment)) {
1033                     Slog.w(TAG, "Ignoring wrong segment for a PerformStep: " + segment);
1034                     return skipToNextSteps(/* segmentsSkipped= */ 1);
1035                 }
1036 
1037                 PrebakedSegment prebaked = (PrebakedSegment) segment;
1038                 if (DEBUG) {
1039                     Slog.d(TAG, "Perform " + VibrationEffect.effectIdToString(
1040                             prebaked.getEffectId()) + " on vibrator "
1041                             + controller.getVibratorInfo().getId());
1042                 }
1043 
1044                 VibrationEffect fallback = mVibration.getFallback(prebaked.getEffectId());
1045                 mVibratorOnResult = controller.on(prebaked, mVibration.id);
1046 
1047                 if (mVibratorOnResult == 0 && prebaked.shouldFallback()
1048                         && (fallback instanceof VibrationEffect.Composed)) {
1049                     if (DEBUG) {
1050                         Slog.d(TAG, "Playing fallback for effect "
1051                                 + VibrationEffect.effectIdToString(prebaked.getEffectId()));
1052                     }
1053                     SingleVibratorStep fallbackStep = nextVibrateStep(startTime, controller,
1054                             replaceCurrentSegment((VibrationEffect.Composed) fallback),
1055                             segmentIndex, vibratorOffTimeout);
1056                     List<Step> fallbackResult = fallbackStep.play();
1057                     // Update the result with the fallback result so this step is seamlessly
1058                     // replaced by the fallback to any outer application of this.
1059                     mVibratorOnResult = fallbackStep.getVibratorOnDuration();
1060                     return fallbackResult;
1061                 }
1062 
1063                 return nextSteps(/* segmentsPlayed= */ 1);
1064             } finally {
1065                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1066             }
1067         }
1068 
1069         /**
1070          * Replace segment at {@link #segmentIndex} in {@link #effect} with given fallback segments.
1071          *
1072          * @return a copy of {@link #effect} with replaced segment.
1073          */
replaceCurrentSegment(VibrationEffect.Composed fallback)1074         private VibrationEffect.Composed replaceCurrentSegment(VibrationEffect.Composed fallback) {
1075             List<VibrationEffectSegment> newSegments = new ArrayList<>(effect.getSegments());
1076             int newRepeatIndex = effect.getRepeatIndex();
1077             newSegments.remove(segmentIndex);
1078             newSegments.addAll(segmentIndex, fallback.getSegments());
1079             if (segmentIndex < effect.getRepeatIndex()) {
1080                 newRepeatIndex += fallback.getSegments().size();
1081             }
1082             return new VibrationEffect.Composed(newSegments, newRepeatIndex);
1083         }
1084     }
1085 
1086     /**
1087      * Represent a step turn the vibrator on using a composition of primitives.
1088      *
1089      * <p>This step will use the maximum supported number of consecutive segments of type
1090      * {@link PrimitiveSegment} starting at the current index.
1091      */
1092     private final class ComposePrimitivesStep extends SingleVibratorStep {
1093 
ComposePrimitivesStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)1094         ComposePrimitivesStep(long startTime, VibratorController controller,
1095                 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
1096             // This step should wait for the last vibration to finish (with the timeout) and for the
1097             // intended step start time (to respect the effect delays).
1098             super(Math.max(startTime, vibratorOffTimeout), controller, effect, index,
1099                     vibratorOffTimeout);
1100         }
1101 
1102         @Override
play()1103         public List<Step> play() {
1104             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep");
1105             try {
1106                 // Load the next PrimitiveSegments to create a single compose call to the vibrator,
1107                 // limited to the vibrator composition maximum size.
1108                 int limit = controller.getVibratorInfo().getCompositionSizeMax();
1109                 int segmentCount = limit > 0
1110                         ? Math.min(effect.getSegments().size(), segmentIndex + limit)
1111                         : effect.getSegments().size();
1112                 List<PrimitiveSegment> primitives = new ArrayList<>();
1113                 for (int i = segmentIndex; i < segmentCount; i++) {
1114                     VibrationEffectSegment segment = effect.getSegments().get(i);
1115                     if (segment instanceof PrimitiveSegment) {
1116                         primitives.add((PrimitiveSegment) segment);
1117                     } else {
1118                         break;
1119                     }
1120                 }
1121 
1122                 if (primitives.isEmpty()) {
1123                     Slog.w(TAG, "Ignoring wrong segment for a ComposePrimitivesStep: "
1124                             + effect.getSegments().get(segmentIndex));
1125                     return skipToNextSteps(/* segmentsSkipped= */ 1);
1126                 }
1127 
1128                 if (DEBUG) {
1129                     Slog.d(TAG, "Compose " + primitives + " primitives on vibrator "
1130                             + controller.getVibratorInfo().getId());
1131                 }
1132                 mVibratorOnResult = controller.on(
1133                         primitives.toArray(new PrimitiveSegment[primitives.size()]),
1134                         mVibration.id);
1135 
1136                 return nextSteps(/* segmentsPlayed= */ primitives.size());
1137             } finally {
1138                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1139             }
1140         }
1141     }
1142 
1143     /**
1144      * Represent a step turn the vibrator on using a composition of PWLE segments.
1145      *
1146      * <p>This step will use the maximum supported number of consecutive segments of type
1147      * {@link StepSegment} or {@link RampSegment} starting at the current index.
1148      */
1149     private final class ComposePwleStep extends SingleVibratorStep {
1150 
ComposePwleStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)1151         ComposePwleStep(long startTime, VibratorController controller,
1152                 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
1153             // This step should wait for the last vibration to finish (with the timeout) and for the
1154             // intended step start time (to respect the effect delays).
1155             super(Math.max(startTime, vibratorOffTimeout), controller, effect, index,
1156                     vibratorOffTimeout);
1157         }
1158 
1159         @Override
play()1160         public List<Step> play() {
1161             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep");
1162             try {
1163                 // Load the next RampSegments to create a single composePwle call to the vibrator,
1164                 // limited to the vibrator PWLE maximum size.
1165                 int limit = controller.getVibratorInfo().getPwleSizeMax();
1166                 int segmentCount = limit > 0
1167                         ? Math.min(effect.getSegments().size(), segmentIndex + limit)
1168                         : effect.getSegments().size();
1169                 List<RampSegment> pwles = new ArrayList<>();
1170                 for (int i = segmentIndex; i < segmentCount; i++) {
1171                     VibrationEffectSegment segment = effect.getSegments().get(i);
1172                     if (segment instanceof RampSegment) {
1173                         pwles.add((RampSegment) segment);
1174                     } else {
1175                         break;
1176                     }
1177                 }
1178 
1179                 if (pwles.isEmpty()) {
1180                     Slog.w(TAG, "Ignoring wrong segment for a ComposePwleStep: "
1181                             + effect.getSegments().get(segmentIndex));
1182                     return skipToNextSteps(/* segmentsSkipped= */ 1);
1183                 }
1184 
1185                 if (DEBUG) {
1186                     Slog.d(TAG, "Compose " + pwles + " PWLEs on vibrator "
1187                             + controller.getVibratorInfo().getId());
1188                 }
1189                 mVibratorOnResult = controller.on(pwles.toArray(new RampSegment[pwles.size()]),
1190                         mVibration.id);
1191 
1192                 return nextSteps(/* segmentsPlayed= */ pwles.size());
1193             } finally {
1194                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1195             }
1196         }
1197     }
1198 
1199     /**
1200      * Represents a step to complete a {@link VibrationEffect}.
1201      *
1202      * <p>This runs right at the time the vibration is considered to end and will update the pending
1203      * vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude.
1204      */
1205     private final class CompleteStep extends SingleVibratorStep {
1206         private final boolean mCancelled;
1207 
CompleteStep(long startTime, boolean cancelled, VibratorController controller, long vibratorOffTimeout)1208         CompleteStep(long startTime, boolean cancelled, VibratorController controller,
1209                 long vibratorOffTimeout) {
1210             super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
1211             mCancelled = cancelled;
1212         }
1213 
1214         @Override
isCleanUp()1215         public boolean isCleanUp() {
1216             // If the vibration was cancelled then this is just a clean up to ramp off the vibrator.
1217             // Otherwise this step is part of the vibration.
1218             return mCancelled;
1219         }
1220 
1221         @Override
cancel()1222         public List<Step> cancel() {
1223             if (mCancelled) {
1224                 // Double cancelling will just turn off the vibrator right away.
1225                 return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
1226             }
1227             return super.cancel();
1228         }
1229 
1230         @Override
play()1231         public List<Step> play() {
1232             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteStep");
1233             try {
1234                 if (DEBUG) {
1235                     Slog.d(TAG, "Running " + (mCancelled ? "cancel" : "complete") + " vibration"
1236                             + " step on vibrator " + controller.getVibratorInfo().getId());
1237                 }
1238                 if (mVibratorCallbackReceived) {
1239                     // Vibration completion callback was received by this step, just turn if off
1240                     // and skip any clean-up.
1241                     stopVibrating();
1242                     return EMPTY_STEP_LIST;
1243                 }
1244 
1245                 float currentAmplitude = controller.getCurrentAmplitude();
1246                 long remainingOnDuration =
1247                         vibratorOffTimeout - CALLBACKS_EXTRA_TIMEOUT - SystemClock.uptimeMillis();
1248                 long rampDownDuration =
1249                         Math.min(remainingOnDuration, mVibrationSettings.getRampDownDuration());
1250                 long stepDownDuration = mVibrationSettings.getRampStepDuration();
1251                 if (currentAmplitude < RAMP_OFF_AMPLITUDE_MIN
1252                         || rampDownDuration <= stepDownDuration) {
1253                     // No need to ramp down the amplitude, just wait to turn it off.
1254                     if (mCancelled) {
1255                         // Vibration is completing because it was cancelled, turn off right away.
1256                         stopVibrating();
1257                         return EMPTY_STEP_LIST;
1258                     } else {
1259                         return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
1260                     }
1261                 }
1262 
1263                 if (DEBUG) {
1264                     Slog.d(TAG, "Ramping down vibrator " + controller.getVibratorInfo().getId()
1265                             + " from amplitude " + currentAmplitude
1266                             + " for " + rampDownDuration + "ms");
1267                 }
1268                 float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration);
1269                 float amplitudeTarget = currentAmplitude - amplitudeDelta;
1270                 long newVibratorOffTimeout = mCancelled ? rampDownDuration : vibratorOffTimeout;
1271                 return Arrays.asList(new RampOffStep(startTime, amplitudeTarget, amplitudeDelta,
1272                         controller, newVibratorOffTimeout));
1273             } finally {
1274                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1275             }
1276         }
1277     }
1278 
1279     /** Represents a step to ramp down the vibrator amplitude before turning it off. */
1280     private final class RampOffStep extends SingleVibratorStep {
1281         private final float mAmplitudeTarget;
1282         private final float mAmplitudeDelta;
1283 
RampOffStep(long startTime, float amplitudeTarget, float amplitudeDelta, VibratorController controller, long vibratorOffTimeout)1284         RampOffStep(long startTime, float amplitudeTarget, float amplitudeDelta,
1285                 VibratorController controller, long vibratorOffTimeout) {
1286             super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout);
1287             mAmplitudeTarget = amplitudeTarget;
1288             mAmplitudeDelta = amplitudeDelta;
1289         }
1290 
1291         @Override
isCleanUp()1292         public boolean isCleanUp() {
1293             return true;
1294         }
1295 
1296         @Override
cancel()1297         public List<Step> cancel() {
1298             return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
1299         }
1300 
1301         @Override
play()1302         public List<Step> play() {
1303             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffStep");
1304             try {
1305                 if (DEBUG) {
1306                     long latency = SystemClock.uptimeMillis() - startTime;
1307                     Slog.d(TAG, "Ramp down the vibrator amplitude, step with "
1308                             + latency + "ms latency.");
1309                 }
1310                 if (mVibratorCallbackReceived) {
1311                     // Vibration completion callback was received by this step, just turn if off
1312                     // and skip the rest of the steps to ramp down the vibrator amplitude.
1313                     stopVibrating();
1314                     return EMPTY_STEP_LIST;
1315                 }
1316 
1317                 changeAmplitude(mAmplitudeTarget);
1318 
1319                 float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta;
1320                 if (newAmplitudeTarget < RAMP_OFF_AMPLITUDE_MIN) {
1321                     // Vibrator amplitude cannot go further down, just turn it off.
1322                     return Arrays.asList(new OffStep(vibratorOffTimeout, controller));
1323                 }
1324                 return Arrays.asList(new RampOffStep(
1325                         startTime + mVibrationSettings.getRampStepDuration(), newAmplitudeTarget,
1326                         mAmplitudeDelta, controller, vibratorOffTimeout));
1327             } finally {
1328                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1329             }
1330         }
1331     }
1332 
1333     /**
1334      * Represents a step to turn the vibrator off.
1335      *
1336      * <p>This runs after a timeout on the expected time the vibrator should have finished playing,
1337      * and can anticipated by vibrator complete callbacks.
1338      */
1339     private final class OffStep extends SingleVibratorStep {
1340 
OffStep(long startTime, VibratorController controller)1341         OffStep(long startTime, VibratorController controller) {
1342             super(startTime, controller, /* effect= */ null, /* index= */ -1, startTime);
1343         }
1344 
1345         @Override
isCleanUp()1346         public boolean isCleanUp() {
1347             return true;
1348         }
1349 
1350         @Override
cancel()1351         public List<Step> cancel() {
1352             return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller));
1353         }
1354 
1355         @Override
cancelImmediately()1356         public void cancelImmediately() {
1357             stopVibrating();
1358         }
1359 
1360         @Override
play()1361         public List<Step> play() {
1362             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "OffStep");
1363             try {
1364                 stopVibrating();
1365                 return EMPTY_STEP_LIST;
1366             } finally {
1367                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1368             }
1369         }
1370     }
1371 
1372     /**
1373      * Represents a step to turn the vibrator on and change its amplitude.
1374      *
1375      * <p>This step ignores vibration completion callbacks and control the vibrator on/off state
1376      * and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}.
1377      */
1378     private final class AmplitudeStep extends SingleVibratorStep {
1379         private long mNextOffTime;
1380 
AmplitudeStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)1381         AmplitudeStep(long startTime, VibratorController controller,
1382                 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) {
1383             // This step has a fixed startTime coming from the timings of the waveform it's playing.
1384             super(startTime, controller, effect, index, vibratorOffTimeout);
1385             mNextOffTime = vibratorOffTimeout;
1386         }
1387 
1388         @Override
shouldPlayWhenVibratorComplete(int vibratorId)1389         public boolean shouldPlayWhenVibratorComplete(int vibratorId) {
1390             if (controller.getVibratorInfo().getId() == vibratorId) {
1391                 mVibratorCallbackReceived = true;
1392                 mNextOffTime = SystemClock.uptimeMillis();
1393             }
1394             // Timings are tightly controlled here, so only anticipate if the vibrator was supposed
1395             // to be ON but has completed prematurely, to turn it back on as soon as possible.
1396             return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0;
1397         }
1398 
1399         @Override
play()1400         public List<Step> play() {
1401             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "AmplitudeStep");
1402             try {
1403                 long now = SystemClock.uptimeMillis();
1404                 long latency = now - startTime;
1405                 if (DEBUG) {
1406                     Slog.d(TAG, "Running amplitude step with " + latency + "ms latency.");
1407                 }
1408 
1409                 if (mVibratorCallbackReceived && latency < 0) {
1410                     // This step was anticipated because the vibrator turned off prematurely.
1411                     // Turn it back on and return this same step to run at the exact right time.
1412                     mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency);
1413                     return Arrays.asList(new AmplitudeStep(startTime, controller, effect,
1414                             segmentIndex, mNextOffTime));
1415                 }
1416 
1417                 VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
1418                 if (!(segment instanceof StepSegment)) {
1419                     Slog.w(TAG, "Ignoring wrong segment for a AmplitudeStep: " + segment);
1420                     return skipToNextSteps(/* segmentsSkipped= */ 1);
1421                 }
1422 
1423                 StepSegment stepSegment = (StepSegment) segment;
1424                 if (stepSegment.getDuration() == 0) {
1425                     // Skip waveform entries with zero timing.
1426                     return skipToNextSteps(/* segmentsSkipped= */ 1);
1427                 }
1428 
1429                 float amplitude = stepSegment.getAmplitude();
1430                 if (amplitude == 0) {
1431                     if (vibratorOffTimeout > now) {
1432                         // Amplitude cannot be set to zero, so stop the vibrator.
1433                         stopVibrating();
1434                         mNextOffTime = now;
1435                     }
1436                 } else {
1437                     if (startTime >= mNextOffTime) {
1438                         // Vibrator is OFF. Turn vibrator back on for the duration of another
1439                         // cycle before setting the amplitude.
1440                         long onDuration = getVibratorOnDuration(effect, segmentIndex);
1441                         if (onDuration > 0) {
1442                             mVibratorOnResult = startVibrating(onDuration);
1443                             mNextOffTime = now + onDuration + CALLBACKS_EXTRA_TIMEOUT;
1444                         }
1445                     }
1446                     changeAmplitude(amplitude);
1447                 }
1448 
1449                 // Use original startTime to avoid propagating latencies to the waveform.
1450                 long nextStartTime = startTime + segment.getDuration();
1451                 return nextSteps(nextStartTime, mNextOffTime, /* segmentsPlayed= */ 1);
1452             } finally {
1453                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1454             }
1455         }
1456 
turnVibratorBackOn(long remainingDuration)1457         private long turnVibratorBackOn(long remainingDuration) {
1458             long onDuration = getVibratorOnDuration(effect, segmentIndex);
1459             if (onDuration <= 0) {
1460                 // Vibrator is supposed to go back off when this step starts, so just leave it off.
1461                 return vibratorOffTimeout;
1462             }
1463             onDuration += remainingDuration;
1464             float expectedAmplitude = controller.getCurrentAmplitude();
1465             mVibratorOnResult = startVibrating(onDuration);
1466             if (mVibratorOnResult > 0) {
1467                 // Set the amplitude back to the value it was supposed to be playing at.
1468                 changeAmplitude(expectedAmplitude);
1469             }
1470             return SystemClock.uptimeMillis() + onDuration + CALLBACKS_EXTRA_TIMEOUT;
1471         }
1472 
startVibrating(long duration)1473         private long startVibrating(long duration) {
1474             if (DEBUG) {
1475                 Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId() + " for "
1476                         + duration + "ms");
1477             }
1478             return controller.on(duration, mVibration.id);
1479         }
1480 
1481         /**
1482          * Get the duration the vibrator will be on for a waveform, starting at {@code startIndex}
1483          * until the next time it's vibrating amplitude is zero or a different type of segment is
1484          * found.
1485          */
getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex)1486         private long getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex) {
1487             List<VibrationEffectSegment> segments = effect.getSegments();
1488             int segmentCount = segments.size();
1489             int repeatIndex = effect.getRepeatIndex();
1490             int i = startIndex;
1491             long timing = 0;
1492             while (i < segmentCount) {
1493                 VibrationEffectSegment segment = segments.get(i);
1494                 if (!(segment instanceof StepSegment)
1495                         || ((StepSegment) segment).getAmplitude() == 0) {
1496                     break;
1497                 }
1498                 timing += segment.getDuration();
1499                 i++;
1500                 if (i == segmentCount && repeatIndex >= 0) {
1501                     i = repeatIndex;
1502                     // prevent infinite loop
1503                     repeatIndex = -1;
1504                 }
1505                 if (i == startIndex) {
1506                     // The repeating waveform keeps the vibrator ON all the time. Use a minimum
1507                     // of 1s duration to prevent short patterns from turning the vibrator ON too
1508                     // frequently.
1509                     return Math.max(timing, 1000);
1510                 }
1511             }
1512             if (i == segmentCount && effect.getRepeatIndex() < 0) {
1513                 // Vibration ending at non-zero amplitude, add extra timings to ramp down after
1514                 // vibration is complete.
1515                 timing += mVibrationSettings.getRampDownDuration();
1516             }
1517             return timing;
1518         }
1519     }
1520 
1521     /**
1522      * Map a {@link CombinedVibration} to the vibrators available on the device.
1523      *
1524      * <p>This contains the logic to find the capabilities required from {@link IVibratorManager} to
1525      * play all of the effects in sync.
1526      */
1527     private final class DeviceEffectMap {
1528         private final SparseArray<VibrationEffect.Composed> mVibratorEffects;
1529         private final int[] mVibratorIds;
1530         private final long mRequiredSyncCapabilities;
1531 
DeviceEffectMap(CombinedVibration.Mono mono)1532         DeviceEffectMap(CombinedVibration.Mono mono) {
1533             mVibratorEffects = new SparseArray<>(mVibrators.size());
1534             mVibratorIds = new int[mVibrators.size()];
1535             for (int i = 0; i < mVibrators.size(); i++) {
1536                 int vibratorId = mVibrators.keyAt(i);
1537                 VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo();
1538                 VibrationEffect effect = mDeviceEffectAdapter.apply(mono.getEffect(), vibratorInfo);
1539                 if (effect instanceof VibrationEffect.Composed) {
1540                     mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
1541                     mVibratorIds[i] = vibratorId;
1542                 }
1543             }
1544             mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
1545         }
1546 
DeviceEffectMap(CombinedVibration.Stereo stereo)1547         DeviceEffectMap(CombinedVibration.Stereo stereo) {
1548             SparseArray<VibrationEffect> stereoEffects = stereo.getEffects();
1549             mVibratorEffects = new SparseArray<>();
1550             for (int i = 0; i < stereoEffects.size(); i++) {
1551                 int vibratorId = stereoEffects.keyAt(i);
1552                 if (mVibrators.contains(vibratorId)) {
1553                     VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo();
1554                     VibrationEffect effect = mDeviceEffectAdapter.apply(
1555                             stereoEffects.valueAt(i), vibratorInfo);
1556                     if (effect instanceof VibrationEffect.Composed) {
1557                         mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect);
1558                     }
1559                 }
1560             }
1561             mVibratorIds = new int[mVibratorEffects.size()];
1562             for (int i = 0; i < mVibratorEffects.size(); i++) {
1563                 mVibratorIds[i] = mVibratorEffects.keyAt(i);
1564             }
1565             mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects);
1566         }
1567 
1568         /**
1569          * Return the number of vibrators mapped to play the {@link CombinedVibration} on this
1570          * device.
1571          */
size()1572         public int size() {
1573             return mVibratorIds.length;
1574         }
1575 
1576         /**
1577          * Return all capabilities required to play the {@link CombinedVibration} in
1578          * between calls to {@link IVibratorManager#prepareSynced} and
1579          * {@link IVibratorManager#triggerSynced}.
1580          */
getRequiredSyncCapabilities()1581         public long getRequiredSyncCapabilities() {
1582             return mRequiredSyncCapabilities;
1583         }
1584 
1585         /** Return all vibrator ids mapped to play the {@link CombinedVibration}. */
getVibratorIds()1586         public int[] getVibratorIds() {
1587             return mVibratorIds;
1588         }
1589 
1590         /** Return the id of the vibrator at given index. */
vibratorIdAt(int index)1591         public int vibratorIdAt(int index) {
1592             return mVibratorEffects.keyAt(index);
1593         }
1594 
1595         /** Return the {@link VibrationEffect} at given index. */
effectAt(int index)1596         public VibrationEffect.Composed effectAt(int index) {
1597             return mVibratorEffects.valueAt(index);
1598         }
1599 
1600         /**
1601          * Return all capabilities required from the {@link IVibratorManager} to prepare and
1602          * trigger all given effects in sync.
1603          *
1604          * @return {@link IVibratorManager#CAP_SYNC} together with all required
1605          * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities.
1606          */
calculateRequiredSyncCapabilities( SparseArray<VibrationEffect.Composed> effects)1607         private long calculateRequiredSyncCapabilities(
1608                 SparseArray<VibrationEffect.Composed> effects) {
1609             long prepareCap = 0;
1610             for (int i = 0; i < effects.size(); i++) {
1611                 VibrationEffectSegment firstSegment = effects.valueAt(i).getSegments().get(0);
1612                 if (firstSegment instanceof StepSegment) {
1613                     prepareCap |= IVibratorManager.CAP_PREPARE_ON;
1614                 } else if (firstSegment instanceof PrebakedSegment) {
1615                     prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM;
1616                 } else if (firstSegment instanceof PrimitiveSegment) {
1617                     prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE;
1618                 }
1619             }
1620             int triggerCap = 0;
1621             if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) {
1622                 triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON;
1623             }
1624             if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) {
1625                 triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM;
1626             }
1627             if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) {
1628                 triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE;
1629             }
1630             return IVibratorManager.CAP_SYNC | prepareCap | triggerCap;
1631         }
1632 
1633         /**
1634          * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with
1635          * different ones, requiring a mixed trigger capability from the vibrator manager for
1636          * syncing all effects.
1637          */
requireMixedTriggerCapability(long prepareCapabilities, long capability)1638         private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) {
1639             return (prepareCapabilities & capability) != 0
1640                     && (prepareCapabilities & ~capability) != 0;
1641         }
1642     }
1643 }
1644