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