• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.os.IBinder;
22 import android.os.PowerManager;
23 import android.os.Process;
24 import android.os.RemoteException;
25 import android.os.SystemClock;
26 import android.os.Trace;
27 import android.os.WorkSource;
28 import android.util.Slog;
29 
30 import com.android.internal.annotations.GuardedBy;
31 import com.android.internal.annotations.VisibleForTesting;
32 
33 import java.util.NoSuchElementException;
34 import java.util.Objects;
35 
36 /** Plays a {@link Vibration} in dedicated thread. */
37 final class VibrationThread extends Thread {
38     static final String TAG = "VibrationThread";
39     static final boolean DEBUG = false;
40 
41     /** Calls into VibratorManager functionality needed for playing a {@link Vibration}. */
42     interface VibratorManagerHooks {
43 
44         /**
45          * Request the manager to prepare for triggering a synchronized vibration step.
46          *
47          * @param requiredCapabilities The required syncing capabilities for this preparation step.
48          *                             Expect CAP_SYNC and a combination of values from
49          *                             IVibratorManager.CAP_PREPARE_* and
50          *                             IVibratorManager.CAP_MIXED_TRIGGER_*.
51          * @param vibratorIds          The id of the vibrators to be prepared.
52          */
prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds)53         boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds);
54 
55         /**
56          * Request the manager to trigger a synchronized vibration. The vibration must already
57          * have been prepared with {@link #prepareSyncedVibration}.
58          */
triggerSyncedVibration(long vibrationId)59         boolean triggerSyncedVibration(long vibrationId);
60 
61         /** Tell the manager to cancel a synced vibration. */
cancelSyncedVibration()62         void cancelSyncedVibration();
63 
64         /**
65          * Record that a vibrator was turned on, and may remain on for the specified duration,
66          * on behalf of the given uid.
67          */
noteVibratorOn(int uid, long duration)68         void noteVibratorOn(int uid, long duration);
69 
70         /** Record that a vibrator was turned off, on behalf of the given uid. */
noteVibratorOff(int uid)71         void noteVibratorOff(int uid);
72 
73         /**
74          * Tell the manager that the currently active vibration has completed its vibration, from
75          * the perspective of the Effect. However, the VibrationThread may still be continuing with
76          * cleanup tasks, and should not be given new work until {@link #onVibrationThreadReleased}
77          * is called.
78          */
onVibrationCompleted(long vibrationId, @NonNull Vibration.EndInfo vibrationEndInfo)79         void onVibrationCompleted(long vibrationId, @NonNull Vibration.EndInfo vibrationEndInfo);
80 
81         /**
82          * Tells the manager that the VibrationThread is finished with the previous vibration and
83          * all of its cleanup tasks, and the vibrators can now be used for another vibration.
84          */
onVibrationThreadReleased(long vibrationId)85         void onVibrationThreadReleased(long vibrationId);
86     }
87 
88     private final PowerManager.WakeLock mWakeLock;
89     private final VibrationThread.VibratorManagerHooks mVibratorManagerHooks;
90 
91     // mLock is used here to communicate that the thread's work status has changed. The
92     // VibrationThread is expected to wait until work arrives, and other threads may wait until
93     // work has finished. Therefore, any changes to the conductor must be followed by a notifyAll
94     // so that threads check if their desired state is achieved.
95     private final Object mLock = new Object();
96 
97     /**
98      * The conductor that is intended to be active. Null value means that a new conductor can
99      * be set to run. Note that this field is only reset to null when mExecutingConductor has
100      * completed, so the two fields should be in sync.
101      */
102     @GuardedBy("mLock")
103     @Nullable
104     private VibrationStepConductor mRequestedActiveConductor;
105 
106     /**
107      * The conductor being executed by this thread, should only be accessed within this thread's
108      * execution. i.e. not thread-safe. {@link #mRequestedActiveConductor} is for cross-thread
109      * signalling.
110      */
111     @Nullable
112     private VibrationStepConductor mExecutingConductor;
113 
114     // Variable only set and read in main thread, no need to lock.
115     private boolean mCalledVibrationCompleteCallback = false;
116 
VibrationThread(PowerManager.WakeLock wakeLock, VibratorManagerHooks vibratorManagerHooks)117     VibrationThread(PowerManager.WakeLock wakeLock, VibratorManagerHooks vibratorManagerHooks) {
118         mWakeLock = wakeLock;
119         mVibratorManagerHooks = vibratorManagerHooks;
120     }
121 
122     /**
123      * Sets/activates the current vibration. Must only be called after receiving
124      * onVibratorsReleased from the previous vibration.
125      *
126      * @return false if VibrationThread couldn't accept it, which shouldn't happen unless called
127      *  before the release callback.
128      */
runVibrationOnVibrationThread(VibrationStepConductor conductor)129     boolean runVibrationOnVibrationThread(VibrationStepConductor conductor) {
130         synchronized (mLock) {
131             if (mRequestedActiveConductor != null) {
132                 Slog.wtf(TAG, "Attempt to start vibration when one already running");
133                 return false;
134             }
135             mRequestedActiveConductor = conductor;
136             mLock.notifyAll();
137         }
138         return true;
139     }
140 
141     @Override
run()142     public void run() {
143         Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
144         while (true) {
145             // mExecutingConductor is only modified in this loop.
146             mExecutingConductor = Objects.requireNonNull(waitForVibrationRequest());
147 
148             mCalledVibrationCompleteCallback = false;
149             runCurrentVibrationWithWakeLock();
150             if (!mExecutingConductor.isFinished()) {
151                 Slog.wtf(TAG, "VibrationThread terminated with unfinished vibration");
152             }
153             synchronized (mLock) {
154                 // Allow another vibration to be requested.
155                 mRequestedActiveConductor = null;
156             }
157             // The callback is run without holding the lock, as it may initiate another vibration.
158             // It's safe to notify even if mVibratorConductor has been re-written, as the "wait"
159             // methods all verify their waited state before returning. In reality though, if the
160             // manager is waiting for the thread to finish, then there is no pending vibration
161             // for this thread.
162             // No point doing this in finally, as if there's an exception, this thread will die
163             // and be unusable anyway.
164             mVibratorManagerHooks.onVibrationThreadReleased(mExecutingConductor.getVibration().id);
165             synchronized (mLock) {
166                 mLock.notifyAll();
167             }
168             mExecutingConductor = null;
169         }
170     }
171 
172     /**
173      * Waits until the VibrationThread has finished processing, timing out after the given
174      * number of milliseconds. In general, external locking will manage the ordering of this
175      * with calls to {@link #runVibrationOnVibrationThread}.
176      *
177      * @return true if the vibration completed, or false if waiting timed out.
178      */
waitForThreadIdle(long maxWaitMillis)179     public boolean waitForThreadIdle(long maxWaitMillis) {
180         long now = SystemClock.elapsedRealtime();
181         long deadline = now + maxWaitMillis;
182         synchronized (mLock) {
183             while (true) {
184                 if (mRequestedActiveConductor == null) {
185                     return true;  // Done
186                 }
187                 if (now >= deadline) {  // Note that thread.wait(0) waits indefinitely.
188                     return false;  // Timed out.
189                 }
190                 try {
191                     mLock.wait(deadline - now);
192                 } catch (InterruptedException e) {
193                     Slog.w(TAG, "VibrationThread interrupted waiting to stop, continuing");
194                 }
195                 now = SystemClock.elapsedRealtime();
196             }
197         }
198     }
199 
200     /** Waits for a signal indicating a vibration is ready to run, then returns its conductor. */
201     @NonNull
waitForVibrationRequest()202     private VibrationStepConductor waitForVibrationRequest() {
203         while (true) {
204             synchronized (mLock) {
205                 if (mRequestedActiveConductor != null) {
206                     return mRequestedActiveConductor;
207                 }
208                 try {
209                     mLock.wait();
210                 } catch (InterruptedException e) {
211                     Slog.w(TAG, "VibrationThread interrupted waiting to start, continuing");
212                 }
213             }
214         }
215     }
216 
217     /**
218      * Only for testing: this method relies on the requested-active conductor, rather than
219      * the executing conductor that's not intended for other threads.
220      *
221      * @return true if the vibration that's currently desired to be active has the given id.
222      */
223     @VisibleForTesting
isRunningVibrationId(long id)224     boolean isRunningVibrationId(long id) {
225         synchronized (mLock) {
226             return (mRequestedActiveConductor != null
227                     && mRequestedActiveConductor.getVibration().id == id);
228         }
229     }
230 
231     /** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
runCurrentVibrationWithWakeLock()232     private void runCurrentVibrationWithWakeLock() {
233         WorkSource workSource = new WorkSource(mExecutingConductor.getVibration().uid);
234         mWakeLock.setWorkSource(workSource);
235         mWakeLock.acquire();
236         try {
237             try {
238                 runCurrentVibrationWithWakeLockAndDeathLink();
239             } finally {
240                 clientVibrationCompleteIfNotAlready(
241                         new Vibration.EndInfo(Vibration.Status.FINISHED_UNEXPECTED));
242             }
243         } finally {
244             mWakeLock.release();
245             mWakeLock.setWorkSource(null);
246         }
247     }
248 
249     /**
250      * Runs the VibrationThread with the binder death link, handling link/unlink failures.
251      * Called from within runWithWakeLock.
252      */
runCurrentVibrationWithWakeLockAndDeathLink()253     private void runCurrentVibrationWithWakeLockAndDeathLink() {
254         IBinder vibrationBinderToken = mExecutingConductor.getVibration().token;
255         try {
256             vibrationBinderToken.linkToDeath(mExecutingConductor, 0);
257         } catch (RemoteException e) {
258             Slog.e(TAG, "Error linking vibration to token death", e);
259             clientVibrationCompleteIfNotAlready(
260                     new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_TOKEN));
261             return;
262         }
263         // Ensure that the unlink always occurs now.
264         try {
265             // This is the actual execution of the vibration.
266             playVibration();
267         } finally {
268             try {
269                 vibrationBinderToken.unlinkToDeath(mExecutingConductor, 0);
270             } catch (NoSuchElementException e) {
271                 Slog.wtf(TAG, "Failed to unlink token", e);
272             }
273         }
274     }
275 
276     // Indicate that the vibration is complete. This can be called multiple times only for
277     // convenience of handling error conditions - an error after the client is complete won't
278     // affect the status.
clientVibrationCompleteIfNotAlready(@onNull Vibration.EndInfo vibrationEndInfo)279     private void clientVibrationCompleteIfNotAlready(@NonNull Vibration.EndInfo vibrationEndInfo) {
280         if (!mCalledVibrationCompleteCallback) {
281             mCalledVibrationCompleteCallback = true;
282             mVibratorManagerHooks.onVibrationCompleted(
283                     mExecutingConductor.getVibration().id, vibrationEndInfo);
284         }
285     }
286 
playVibration()287     private void playVibration() {
288         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
289         try {
290             mExecutingConductor.prepareToStart();
291             while (!mExecutingConductor.isFinished()) {
292                 boolean readyToRun = mExecutingConductor.waitUntilNextStepIsDue();
293                 // If we waited, don't run the next step, but instead re-evaluate status.
294                 if (readyToRun) {
295                     if (DEBUG) {
296                         Slog.d(TAG, "Play vibration consuming next step...");
297                     }
298                     // Run the step without holding the main lock, to avoid HAL interactions from
299                     // blocking the thread.
300                     mExecutingConductor.runNextStep();
301                 }
302 
303                 if (!mCalledVibrationCompleteCallback) {
304                     // This block can only run once due to mCalledVibrationCompleteCallback.
305                     Vibration.EndInfo vibrationEndInfo =
306                             mExecutingConductor.calculateVibrationEndInfo();
307                     if (vibrationEndInfo != null) {
308                         // First time vibration stopped running, start clean-up tasks and notify
309                         // callback immediately.
310                         clientVibrationCompleteIfNotAlready(vibrationEndInfo);
311                     }
312                 }
313             }
314         } finally {
315             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
316         }
317     }
318 }
319