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.os.SystemClock;
20 import android.os.VibrationEffect;
21 import android.util.Slog;
22 
23 import java.util.Arrays;
24 import java.util.List;
25 
26 /**
27  * Represent a step on a single vibrator that plays one or more segments from a
28  * {@link VibrationEffect.Composed} effect.
29  */
30 abstract class AbstractVibratorStep extends Step {
31     public final VibratorController controller;
32     public final VibrationEffect.Composed effect;
33     public final int segmentIndex;
34 
35     long mVibratorOnResult;
36     long mPendingVibratorOffDeadline;
37     boolean mVibratorCompleteCallbackReceived;
38 
39     /**
40      * @param conductor          The VibrationStepConductor for these steps.
41      * @param startTime          The time to schedule this step in the
42      *                           {@link VibrationStepConductor}.
43      * @param controller         The vibrator that is playing the effect.
44      * @param effect             The effect being played in this step.
45      * @param index              The index of the next segment to be played by this step
46      * @param pendingVibratorOffDeadline The time the vibrator is expected to complete any
47      *                           previous vibration and turn off. This is used to allow this step to
48      *                           be triggered when the completion callback is received, and can
49      *                           be used to play effects back-to-back.
50      */
AbstractVibratorStep(VibrationStepConductor conductor, long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long pendingVibratorOffDeadline)51     AbstractVibratorStep(VibrationStepConductor conductor, long startTime,
52             VibratorController controller, VibrationEffect.Composed effect, int index,
53             long pendingVibratorOffDeadline) {
54         super(conductor, startTime);
55         this.controller = controller;
56         this.effect = effect;
57         this.segmentIndex = index;
58         mPendingVibratorOffDeadline = pendingVibratorOffDeadline;
59     }
60 
getVibratorId()61     public int getVibratorId() {
62         return controller.getVibratorInfo().getId();
63     }
64 
65     @Override
getVibratorOnDuration()66     public long getVibratorOnDuration() {
67         return mVibratorOnResult;
68     }
69 
70     @Override
acceptVibratorCompleteCallback(int vibratorId)71     public boolean acceptVibratorCompleteCallback(int vibratorId) {
72         if (getVibratorId() != vibratorId) {
73             return false;
74         }
75 
76         // Only activate this step if a timeout was set to wait for the vibration to complete,
77         // otherwise we are waiting for the correct time to play the next step.
78         boolean shouldAcceptCallback = mPendingVibratorOffDeadline > SystemClock.uptimeMillis();
79         if (VibrationThread.DEBUG) {
80             Slog.d(VibrationThread.TAG,
81                     "Received completion callback from " + vibratorId
82                             + ", accepted = " + shouldAcceptCallback);
83         }
84 
85         // The callback indicates this vibrator has stopped, reset the timeout.
86         mPendingVibratorOffDeadline = 0;
87         mVibratorCompleteCallbackReceived = true;
88         return shouldAcceptCallback;
89     }
90 
91     @Override
cancel()92     public List<Step> cancel() {
93         return Arrays.asList(new CompleteEffectVibratorStep(conductor, SystemClock.uptimeMillis(),
94                 /* cancelled= */ true, controller, mPendingVibratorOffDeadline));
95     }
96 
97     @Override
cancelImmediately()98     public void cancelImmediately() {
99         if (mPendingVibratorOffDeadline > SystemClock.uptimeMillis()) {
100             // Vibrator might be running from previous steps, so turn it off while canceling.
101             stopVibrating();
102         }
103     }
104 
handleVibratorOnResult(long vibratorOnResult)105     protected long handleVibratorOnResult(long vibratorOnResult) {
106         mVibratorOnResult = vibratorOnResult;
107         if (VibrationThread.DEBUG) {
108             Slog.d(VibrationThread.TAG,
109                     "Turned on vibrator " + getVibratorId() + ", result = " + mVibratorOnResult);
110         }
111         if (mVibratorOnResult > 0) {
112             // Vibrator was turned on by this step, with vibratorOnResult as the duration.
113             // Set an extra timeout to wait for the vibrator completion callback.
114             mPendingVibratorOffDeadline = SystemClock.uptimeMillis() + mVibratorOnResult
115                     + VibrationStepConductor.CALLBACKS_EXTRA_TIMEOUT;
116         } else {
117             // Vibrator does not support the request or failed to turn on, reset callback deadline.
118             mPendingVibratorOffDeadline = 0;
119         }
120         return mVibratorOnResult;
121     }
122 
stopVibrating()123     protected void stopVibrating() {
124         if (VibrationThread.DEBUG) {
125             Slog.d(VibrationThread.TAG,
126                     "Turning off vibrator " + getVibratorId());
127         }
128         controller.off();
129         getVibration().stats.reportVibratorOff();
130         mPendingVibratorOffDeadline = 0;
131     }
132 
changeAmplitude(float amplitude)133     protected void changeAmplitude(float amplitude) {
134         if (VibrationThread.DEBUG) {
135             Slog.d(VibrationThread.TAG,
136                     "Amplitude changed on vibrator " + getVibratorId() + " to " + amplitude);
137         }
138         controller.setAmplitude(amplitude);
139         getVibration().stats.reportSetAmplitude();
140     }
141 
142     /**
143      * Return the {@link VibrationStepConductor#nextVibrateStep} with start and off timings
144      * calculated from {@link #getVibratorOnDuration()} based on the current
145      * {@link SystemClock#uptimeMillis()} and jumping all played segments from the effect.
146      */
nextSteps(int segmentsPlayed)147     protected List<Step> nextSteps(int segmentsPlayed) {
148         // Schedule next steps to run right away.
149         long nextStartTime = SystemClock.uptimeMillis();
150         if (mVibratorOnResult > 0) {
151             // Vibrator was turned on by this step, with mVibratorOnResult as the duration.
152             // Schedule next steps for right after the vibration finishes.
153             nextStartTime += mVibratorOnResult;
154         }
155         return nextSteps(nextStartTime, segmentsPlayed);
156     }
157 
158     /**
159      * Return the {@link VibrationStepConductor#nextVibrateStep} with given start time,
160      * which might be calculated independently, and jumping all played segments from the effect.
161      *
162      * <p>This should be used when the vibrator on/off state is not responsible for the step
163      * execution timing, e.g. while playing the vibrator amplitudes.
164      */
nextSteps(long nextStartTime, int segmentsPlayed)165     protected List<Step> nextSteps(long nextStartTime, int segmentsPlayed) {
166         int nextSegmentIndex = segmentIndex + segmentsPlayed;
167         int effectSize = effect.getSegments().size();
168         int repeatIndex = effect.getRepeatIndex();
169         if (nextSegmentIndex >= effectSize && repeatIndex >= 0) {
170             // Count the loops that were played.
171             int loopSize = effectSize - repeatIndex;
172             int loopSegmentsPlayed = nextSegmentIndex - repeatIndex;
173             getVibration().stats.reportRepetition(loopSegmentsPlayed / loopSize);
174             nextSegmentIndex = repeatIndex + ((nextSegmentIndex - effectSize) % loopSize);
175         }
176         Step nextStep = conductor.nextVibrateStep(nextStartTime, controller, effect,
177                 nextSegmentIndex, mPendingVibratorOffDeadline);
178         return nextStep == null ? VibrationStepConductor.EMPTY_STEP_LIST : Arrays.asList(nextStep);
179     }
180 }
181