1 /*
2  * Copyright (C) 2022 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.os.Build;
22 import android.os.CombinedVibration;
23 import android.os.IBinder;
24 import android.os.VibrationEffect;
25 import android.os.vibrator.PrebakedSegment;
26 import android.os.vibrator.PrimitiveSegment;
27 import android.os.vibrator.RampSegment;
28 import android.os.vibrator.VibrationEffectSegment;
29 import android.util.IntArray;
30 import android.util.Slog;
31 import android.util.SparseArray;
32 
33 import com.android.internal.annotations.GuardedBy;
34 
35 import java.util.ArrayList;
36 import java.util.Iterator;
37 import java.util.LinkedList;
38 import java.util.List;
39 import java.util.PriorityQueue;
40 import java.util.Queue;
41 
42 /**
43  * Creates and manages a queue of steps for performing a VibrationEffect, as well as coordinating
44  * dispatch of callbacks.
45  *
46  * <p>In general, methods in this class are intended to be called only by a single instance of
47  * VibrationThread. The only thread-safe methods for calling from other threads are the "notify"
48  * methods (which should never be used from the VibrationThread thread).
49  */
50 final class VibrationStepConductor implements IBinder.DeathRecipient {
51     private static final boolean DEBUG = VibrationThread.DEBUG;
52     private static final String TAG = VibrationThread.TAG;
53 
54     /**
55      * Extra timeout added to the end of each vibration step to ensure it finishes even when
56      * vibrator callbacks are lost.
57      */
58     static final long CALLBACKS_EXTRA_TIMEOUT = 1_000;
59     /** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */
60     static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f;
61     static final List<Step> EMPTY_STEP_LIST = new ArrayList<>();
62 
63     // Used within steps.
64     public final VibrationSettings vibrationSettings;
65     public final DeviceVibrationEffectAdapter deviceEffectAdapter;
66     public final VibrationThread.VibratorManagerHooks vibratorManagerHooks;
67 
68     // Not guarded by lock because they're not modified by this conductor, it's used here only to
69     // check immutable attributes. The status and other mutable states are changed by the service or
70     // by the vibrator steps.
71     private final HalVibration mVibration;
72     private final SparseArray<VibratorController> mVibrators = new SparseArray<>();
73 
74     private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>();
75     private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>();
76 
77     // Signalling fields.
78     // Note that vibrator callback signals may happen inside vibrator HAL calls made by the
79     // VibrationThread, or on an external executor, so this lock should not be held for anything
80     // other than updating signalling state - particularly not during HAL calls or when invoking
81     // other callbacks that may trigger calls into the thread.
82     private final Object mLock = new Object();
83     @GuardedBy("mLock")
84     private final IntArray mSignalVibratorsComplete;
85     @Nullable
86     @GuardedBy("mLock")
87     private Vibration.EndInfo mSignalCancel = null;
88     @GuardedBy("mLock")
89     private boolean mSignalCancelImmediate = false;
90 
91     @Nullable
92     private Vibration.EndInfo mCancelledVibrationEndInfo = null;
93     private boolean mCancelledImmediately = false;  // hard stop
94     private int mPendingVibrateSteps;
95     private int mRemainingStartSequentialEffectSteps;
96     private int mSuccessfulVibratorOnSteps;
97 
VibrationStepConductor(HalVibration vib, VibrationSettings vibrationSettings, DeviceVibrationEffectAdapter effectAdapter, SparseArray<VibratorController> availableVibrators, VibrationThread.VibratorManagerHooks vibratorManagerHooks)98     VibrationStepConductor(HalVibration vib, VibrationSettings vibrationSettings,
99             DeviceVibrationEffectAdapter effectAdapter,
100             SparseArray<VibratorController> availableVibrators,
101             VibrationThread.VibratorManagerHooks vibratorManagerHooks) {
102         this.mVibration = vib;
103         this.vibrationSettings = vibrationSettings;
104         this.deviceEffectAdapter = effectAdapter;
105         this.vibratorManagerHooks = vibratorManagerHooks;
106 
107         CombinedVibration effect = vib.getEffect();
108         for (int i = 0; i < availableVibrators.size(); i++) {
109             if (effect.hasVibrator(availableVibrators.keyAt(i))) {
110                 mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i));
111             }
112         }
113         this.mSignalVibratorsComplete = new IntArray(mVibrators.size());
114     }
115 
116     @Nullable
nextVibrateStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int segmentIndex, long pendingVibratorOffDeadline)117     AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller,
118             VibrationEffect.Composed effect, int segmentIndex, long pendingVibratorOffDeadline) {
119         if (Build.IS_DEBUGGABLE) {
120             expectIsVibrationThread(true);
121         }
122         if (segmentIndex >= effect.getSegments().size()) {
123             segmentIndex = effect.getRepeatIndex();
124         }
125         if (segmentIndex < 0) {
126             // No more segments to play, last step is to complete the vibration on this vibrator.
127             return new CompleteEffectVibratorStep(this, startTime, /* cancelled= */ false,
128                     controller, pendingVibratorOffDeadline);
129         }
130 
131         VibrationEffectSegment segment = effect.getSegments().get(segmentIndex);
132         if (segment instanceof PrebakedSegment) {
133             return new PerformPrebakedVibratorStep(this, startTime, controller, effect,
134                     segmentIndex, pendingVibratorOffDeadline);
135         }
136         if (segment instanceof PrimitiveSegment) {
137             return new ComposePrimitivesVibratorStep(this, startTime, controller, effect,
138                     segmentIndex, pendingVibratorOffDeadline);
139         }
140         if (segment instanceof RampSegment) {
141             return new ComposePwleVibratorStep(this, startTime, controller, effect, segmentIndex,
142                     pendingVibratorOffDeadline);
143         }
144         return new SetAmplitudeVibratorStep(this, startTime, controller, effect, segmentIndex,
145                 pendingVibratorOffDeadline);
146     }
147 
148     /** Called when this conductor is going to be started running by the VibrationThread. */
prepareToStart()149     public void prepareToStart() {
150         if (Build.IS_DEBUGGABLE) {
151             expectIsVibrationThread(true);
152         }
153         CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect());
154         mPendingVibrateSteps++;
155         // This count is decremented at the completion of the step, so we don't subtract one.
156         mRemainingStartSequentialEffectSteps = sequentialEffect.getEffects().size();
157         mNextSteps.offer(new StartSequentialEffectStep(this, sequentialEffect));
158         // Vibration will start playing in the Vibrator, following the effect timings and delays.
159         // Report current time as the vibration start time, for debugging.
160         mVibration.stats.reportStarted();
161     }
162 
getVibration()163     public HalVibration getVibration() {
164         // No thread assertion: immutable
165         return mVibration;
166     }
167 
getVibrators()168     SparseArray<VibratorController> getVibrators() {
169         // No thread assertion: immutable
170         return mVibrators;
171     }
172 
isFinished()173     public boolean isFinished() {
174         if (Build.IS_DEBUGGABLE) {
175             expectIsVibrationThread(true);
176         }
177         if (mCancelledImmediately) {
178             return true;  // Terminate.
179         }
180 
181         // No need to check for vibration complete callbacks - if there were any, they would
182         // have no steps to notify anyway.
183         return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty();
184     }
185 
186     /**
187      * Calculate the {@link Vibration.Status} based on the current queue state and the expected
188      * number of {@link StartSequentialEffectStep} to be played.
189      */
190     @Nullable
calculateVibrationEndInfo()191     public Vibration.EndInfo calculateVibrationEndInfo() {
192         if (Build.IS_DEBUGGABLE) {
193             expectIsVibrationThread(true);
194         }
195 
196         if (mCancelledVibrationEndInfo != null) {
197             return mCancelledVibrationEndInfo;
198         }
199         if (mPendingVibrateSteps > 0 || mRemainingStartSequentialEffectSteps > 0) {
200             // Vibration still running.
201             return null;
202         }
203         // No pending steps, and something happened.
204         if (mSuccessfulVibratorOnSteps > 0) {
205             return new Vibration.EndInfo(Vibration.Status.FINISHED);
206         }
207         // If no step was able to turn the vibrator ON successfully.
208         return new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED);
209     }
210 
211     /**
212      * Blocks until the next step is due to run. The wait here may be interrupted by calling
213      * one of the "notify" methods.
214      *
215      * <p>This method returns true if the next step is ready to run now. If the method returns
216      * false, then some waiting was done, but may have been interrupted by a wakeUp, and the
217      * status and isFinished of the vibration should be re-checked before calling this method again.
218      *
219      * @return true if the next step can be run now or the vibration is finished, or false if this
220      *   method waited and the conductor state may have changed asynchronously, in which case this
221      *   method needs to be run again.
222      */
waitUntilNextStepIsDue()223     public boolean waitUntilNextStepIsDue() {
224         if (Build.IS_DEBUGGABLE) {
225             expectIsVibrationThread(true);
226         }
227 
228         processAllNotifySignals();
229         if (mCancelledImmediately) {
230             // Don't try to run a step for immediate cancel, although there should be none left.
231             // Non-immediate cancellation may have cleanup steps, so it continues processing.
232             return false;
233         }
234         if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
235             return true;  // Resumed step ready.
236         }
237         Step nextStep = mNextSteps.peek();
238         if (nextStep == null) {
239             return true;  // Finished
240         }
241         long waitMillis = nextStep.calculateWaitTime();
242         if (waitMillis <= 0) {
243             return true;  // Regular step ready
244         }
245         synchronized (mLock) {
246             // Double check for signals before sleeping, as their notify wouldn't interrupt a fresh
247             // wait.
248             if (hasPendingNotifySignalLocked()) {
249                 // Don't run the next step, it will loop back to this method and process them.
250                 return false;
251             }
252             try {
253                 mLock.wait(waitMillis);
254             } catch (InterruptedException e) {
255             }
256             return false;  // Caller needs to check isFinished and maybe wait again.
257         }
258     }
259 
260     @Nullable
pollNext()261     private Step pollNext() {
262         if (Build.IS_DEBUGGABLE) {
263             expectIsVibrationThread(true);
264         }
265 
266         // Prioritize the steps resumed by a vibrator complete callback, irrespective of their
267         // "next run time".
268         if (!mPendingOnVibratorCompleteSteps.isEmpty()) {
269             return mPendingOnVibratorCompleteSteps.poll();
270         }
271         return mNextSteps.poll();
272     }
273 
274     /**
275      * Play and remove the step at the top of this queue, and also adds the next steps generated
276      * to be played next.
277      */
runNextStep()278     public void runNextStep() {
279         if (Build.IS_DEBUGGABLE) {
280             expectIsVibrationThread(true);
281         }
282         // In theory a completion callback could have come in between the wait finishing and
283         // this method starting, but that only means the step is due now anyway, so it's reasonable
284         // to run it before processing callbacks as the window is tiny.
285         Step nextStep = pollNext();
286         if (nextStep != null) {
287             if (DEBUG) {
288                 Slog.d(TAG, "Playing vibration id " + getVibration().id
289                         + ((nextStep instanceof AbstractVibratorStep)
290                         ? " on vibrator " + ((AbstractVibratorStep) nextStep).getVibratorId() : "")
291                         + " " + nextStep.getClass().getSimpleName()
292                         + (nextStep.isCleanUp() ? " (cleanup)" : ""));
293             }
294 
295             List<Step> nextSteps = nextStep.play();
296             if (nextStep.getVibratorOnDuration() > 0) {
297                 mSuccessfulVibratorOnSteps++;
298             }
299             if (nextStep instanceof StartSequentialEffectStep) {
300                 mRemainingStartSequentialEffectSteps--;
301             }
302             if (!nextStep.isCleanUp()) {
303                 mPendingVibrateSteps--;
304             }
305             for (int i = 0; i < nextSteps.size(); i++) {
306                 mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1;
307             }
308             mNextSteps.addAll(nextSteps);
309         }
310     }
311 
312     /**
313      * Binder death notification. VibrationThread registers this when it's running a conductor.
314      * Note that cancellation could theoretically happen immediately, before the conductor has
315      * started, but in this case it will be processed in the first signals loop.
316      */
317     @Override
binderDied()318     public void binderDied() {
319         if (DEBUG) {
320             Slog.d(TAG, "Binder died, cancelling vibration...");
321         }
322         notifyCancelled(new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED),
323                 /* immediate= */ false);
324     }
325 
326     /**
327      * Notify the execution that cancellation is requested. This will be acted upon
328      * asynchronously in the VibrationThread.
329      *
330      * <p>Only the first cancel signal will be used to end a cancelled vibration, but subsequent
331      * calls with {@code immediate} flag set to true can still force the first cancel signal to
332      * take effect urgently.
333      *
334      * @param immediate indicates whether cancellation should abort urgently and skip cleanup steps.
335      */
notifyCancelled(@onNull Vibration.EndInfo cancelInfo, boolean immediate)336     public void notifyCancelled(@NonNull Vibration.EndInfo cancelInfo, boolean immediate) {
337         if (Build.IS_DEBUGGABLE) {
338             expectIsVibrationThread(false);
339         }
340         if (DEBUG) {
341             Slog.d(TAG, "Vibration cancel requested with signal=" + cancelInfo
342                     + ", immediate=" + immediate);
343         }
344         if ((cancelInfo == null) || !cancelInfo.status.name().startsWith("CANCEL")) {
345             Slog.w(TAG, "Vibration cancel requested with bad signal=" + cancelInfo
346                     + ", using CANCELLED_UNKNOWN_REASON to ensure cancellation.");
347             cancelInfo = new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_UNKNOWN_REASON);
348         }
349         synchronized (mLock) {
350             if ((immediate && mSignalCancelImmediate) || (mSignalCancel != null)) {
351                 if (DEBUG) {
352                     Slog.d(TAG, "Vibration cancel request ignored as the vibration "
353                             + mVibration.id + "is already being cancelled with signal="
354                             + mSignalCancel + ", immediate=" + mSignalCancelImmediate);
355                 }
356                 return;
357             }
358             mSignalCancelImmediate |= immediate;
359             if (mSignalCancel == null) {
360                 mSignalCancel = cancelInfo;
361             } else {
362                 if (DEBUG) {
363                     Slog.d(TAG, "Vibration cancel request new signal=" + cancelInfo
364                             + " ignored as the vibration was already cancelled with signal="
365                             + mSignalCancel + ", but immediate flag was updated to "
366                             + mSignalCancelImmediate);
367                 }
368             }
369             mLock.notify();
370         }
371     }
372 
373     /**
374      * Notify the conductor that a vibrator has completed its work.
375      *
376      * <p>This is a lightweight method intended to be called directly via native callbacks.
377      * The state update is recorded for processing on the main execution thread (VibrationThread).
378      */
notifyVibratorComplete(int vibratorId)379     public void notifyVibratorComplete(int vibratorId) {
380         // HAL callbacks may be triggered directly within HAL calls, so these notifications
381         // could be on the VibrationThread as it calls the HAL, or some other executor later.
382         // Therefore no thread assertion is made here.
383 
384         if (DEBUG) {
385             Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId);
386         }
387 
388         synchronized (mLock) {
389             mSignalVibratorsComplete.add(vibratorId);
390             mLock.notify();
391         }
392     }
393 
394     /**
395      * Notify that a VibratorManager sync operation has completed.
396      *
397      * <p>This is a lightweight method intended to be called directly via native callbacks.
398      * The state update is recorded for processing on the main execution thread
399      * (VibrationThread).
400      */
notifySyncedVibrationComplete()401     public void notifySyncedVibrationComplete() {
402         // HAL callbacks may be triggered directly within HAL calls, so these notifications
403         // could be on the VibrationThread as it calls the HAL, or some other executor later.
404         // Therefore no thread assertion is made here.
405 
406         if (DEBUG) {
407             Slog.d(TAG, "Synced vibration complete reported by vibrator manager");
408         }
409 
410         synchronized (mLock) {
411             for (int i = 0; i < mVibrators.size(); i++) {
412                 mSignalVibratorsComplete.add(mVibrators.keyAt(i));
413             }
414             mLock.notify();
415         }
416     }
417 
418     /** Returns true if a cancellation signal was sent via {@link #notifyCancelled}. */
wasNotifiedToCancel()419     public boolean wasNotifiedToCancel() {
420         if (Build.IS_DEBUGGABLE) {
421             expectIsVibrationThread(false);
422         }
423         synchronized (mLock) {
424             return mSignalCancel != null;
425         }
426     }
427 
428     @GuardedBy("mLock")
hasPendingNotifySignalLocked()429     private boolean hasPendingNotifySignalLocked() {
430         if (Build.IS_DEBUGGABLE) {
431             expectIsVibrationThread(true);  // Reads VibrationThread variables as well as signals.
432         }
433         return (mSignalCancel != null && mCancelledVibrationEndInfo == null)
434                 || (mSignalCancelImmediate && !mCancelledImmediately)
435                 || (mSignalVibratorsComplete.size() > 0);
436     }
437 
438     /**
439      * Process any notified cross-thread signals, applying the necessary VibrationThread state
440      * changes.
441      */
processAllNotifySignals()442     private void processAllNotifySignals() {
443         if (Build.IS_DEBUGGABLE) {
444             expectIsVibrationThread(true);
445         }
446 
447         int[] vibratorsToProcess = null;
448         Vibration.EndInfo doCancelInfo = null;
449         boolean doCancelImmediate = false;
450         // Collect signals to process, but don't keep the lock while processing them.
451         synchronized (mLock) {
452             if (mSignalCancelImmediate) {
453                 if (mCancelledImmediately) {
454                     Slog.wtf(TAG, "Immediate cancellation signal processed twice");
455                 }
456                 // This should only happen once.
457                 doCancelImmediate = true;
458                 doCancelInfo = mSignalCancel;
459             }
460             if ((mSignalCancel != null) && (mCancelledVibrationEndInfo == null)) {
461                 doCancelInfo = mSignalCancel;
462             }
463             if (!doCancelImmediate && mSignalVibratorsComplete.size() > 0) {
464                 // Swap out the queue of completions to process.
465                 vibratorsToProcess = mSignalVibratorsComplete.toArray();  // makes a copy
466                 mSignalVibratorsComplete.clear();
467             }
468         }
469 
470         // Force cancellation means stop everything and clear all steps, so the execution loop
471         // shouldn't come back to this method. To observe explicitly: this drops vibrator
472         // completion signals that were collected in this call, but we won't process them
473         // anyway as all steps are cancelled.
474         if (doCancelImmediate) {
475             processCancelImmediately(doCancelInfo);
476             return;
477         }
478         if (doCancelInfo != null) {
479             processCancel(doCancelInfo);
480         }
481         if (vibratorsToProcess != null) {
482             processVibratorsComplete(vibratorsToProcess);
483         }
484     }
485 
486     /**
487      * Cancel the current queue, replacing all remaining steps with respective clean-up steps.
488      *
489      * <p>This will remove all steps and replace them with respective results of
490      * {@link Step#cancel()}.
491      */
processCancel(Vibration.EndInfo cancelInfo)492     public void processCancel(Vibration.EndInfo cancelInfo) {
493         if (Build.IS_DEBUGGABLE) {
494             expectIsVibrationThread(true);
495         }
496 
497         mCancelledVibrationEndInfo = cancelInfo;
498         // Vibrator callbacks should wait until all steps from the queue are properly cancelled
499         // and clean up steps are added back to the queue, so they can handle the callback.
500         List<Step> cleanUpSteps = new ArrayList<>();
501         Step step;
502         while ((step = pollNext()) != null) {
503             cleanUpSteps.addAll(step.cancel());
504         }
505         // All steps generated by Step.cancel() should be clean-up steps.
506         mPendingVibrateSteps = 0;
507         mNextSteps.addAll(cleanUpSteps);
508     }
509 
510     /**
511      * Cancel the current queue immediately, clearing all remaining steps and skipping clean-up.
512      *
513      * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order.
514      */
processCancelImmediately(Vibration.EndInfo cancelInfo)515     public void processCancelImmediately(Vibration.EndInfo cancelInfo) {
516         if (Build.IS_DEBUGGABLE) {
517             expectIsVibrationThread(true);
518         }
519 
520         mCancelledImmediately = true;
521         mCancelledVibrationEndInfo = cancelInfo;
522         Step step;
523         while ((step = pollNext()) != null) {
524             step.cancelImmediately();
525         }
526         mPendingVibrateSteps = 0;
527     }
528 
529     /**
530      * Processes the vibrators that have sent their complete callbacks. A step is found that will
531      * accept the completion callback, and this step is brought forward for execution in the next
532      * run.
533      *
534      * <p>This assumes only one of the next steps is waiting on this given vibrator, so the
535      * first step found will be resumed by this method, in no particular order.
536      */
processVibratorsComplete(@onNull int[] vibratorsToProcess)537     private void processVibratorsComplete(@NonNull int[] vibratorsToProcess) {
538         if (Build.IS_DEBUGGABLE) {
539             expectIsVibrationThread(true);
540         }
541 
542         for (int vibratorId : vibratorsToProcess) {
543             Iterator<Step> it = mNextSteps.iterator();
544             while (it.hasNext()) {
545                 Step step = it.next();
546                 if (step.acceptVibratorCompleteCallback(vibratorId)) {
547                     it.remove();
548                     mPendingOnVibratorCompleteSteps.offer(step);
549                     break;
550                 }
551             }
552         }
553     }
554 
toSequential(CombinedVibration effect)555     private static CombinedVibration.Sequential toSequential(CombinedVibration effect) {
556         if (effect instanceof CombinedVibration.Sequential) {
557             return (CombinedVibration.Sequential) effect;
558         }
559         return (CombinedVibration.Sequential) CombinedVibration.startSequential()
560                 .addNext(effect)
561                 .combine();
562     }
563 
564     /**
565      * This check is used for debugging and documentation to indicate the thread that's expected
566      * to invoke a given public method on this class. Most methods are only invoked by
567      * VibrationThread, which is where all the steps and HAL calls should be made. Other threads
568      * should only signal to the execution flow being run by VibrationThread.
569      */
expectIsVibrationThread(boolean isVibrationThread)570     private static void expectIsVibrationThread(boolean isVibrationThread) {
571         if ((Thread.currentThread() instanceof VibrationThread) != isVibrationThread) {
572             Slog.wtfStack("VibrationStepConductor",
573                     "Thread caller assertion failed, expected isVibrationThread="
574                             + isVibrationThread);
575         }
576     }
577 }
578