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 HalVibration} 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 HalVibration}. */
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(
165                     mExecutingConductor.getVibration().id);
166             synchronized (mLock) {
167                 mLock.notifyAll();
168             }
169             mExecutingConductor = null;
170         }
171     }
172 
173     /**
174      * Waits until the VibrationThread has finished processing, timing out after the given
175      * number of milliseconds. In general, external locking will manage the ordering of this
176      * with calls to {@link #runVibrationOnVibrationThread}.
177      *
178      * @return true if the vibration completed, or false if waiting timed out.
179      */
waitForThreadIdle(long maxWaitMillis)180     public boolean waitForThreadIdle(long maxWaitMillis) {
181         long now = SystemClock.elapsedRealtime();
182         long deadline = now + maxWaitMillis;
183         synchronized (mLock) {
184             while (true) {
185                 if (mRequestedActiveConductor == null) {
186                     return true;  // Done
187                 }
188                 if (now >= deadline) {  // Note that thread.wait(0) waits indefinitely.
189                     return false;  // Timed out.
190                 }
191                 try {
192                     mLock.wait(deadline - now);
193                 } catch (InterruptedException e) {
194                     Slog.w(TAG, "VibrationThread interrupted waiting to stop, continuing");
195                 }
196                 now = SystemClock.elapsedRealtime();
197             }
198         }
199     }
200 
201     /** Waits for a signal indicating a vibration is ready to run, then returns its conductor. */
202     @NonNull
waitForVibrationRequest()203     private VibrationStepConductor waitForVibrationRequest() {
204         while (true) {
205             synchronized (mLock) {
206                 if (mRequestedActiveConductor != null) {
207                     return mRequestedActiveConductor;
208                 }
209                 try {
210                     mLock.wait();
211                 } catch (InterruptedException e) {
212                     Slog.w(TAG, "VibrationThread interrupted waiting to start, continuing");
213                 }
214             }
215         }
216     }
217 
218     /**
219      * Only for testing: this method relies on the requested-active conductor, rather than
220      * the executing conductor that's not intended for other threads.
221      *
222      * @return true if the vibration that's currently desired to be active has the given id.
223      */
224     @VisibleForTesting
isRunningVibrationId(long id)225     boolean isRunningVibrationId(long id) {
226         synchronized (mLock) {
227             return (mRequestedActiveConductor != null
228                     && mRequestedActiveConductor.getVibration().id == id);
229         }
230     }
231 
232     /** Runs the VibrationThread ensuring that the wake lock is acquired and released. */
runCurrentVibrationWithWakeLock()233     private void runCurrentVibrationWithWakeLock() {
234         WorkSource workSource = new WorkSource(
235                 mExecutingConductor.getVibration().callerInfo.uid);
236         mWakeLock.setWorkSource(workSource);
237         mWakeLock.acquire();
238         try {
239             try {
240                 runCurrentVibrationWithWakeLockAndDeathLink();
241             } finally {
242                 clientVibrationCompleteIfNotAlready(
243                         new Vibration.EndInfo(Vibration.Status.FINISHED_UNEXPECTED));
244             }
245         } finally {
246             mWakeLock.release();
247             mWakeLock.setWorkSource(null);
248         }
249     }
250 
251     /**
252      * Runs the VibrationThread with the binder death link, handling link/unlink failures.
253      * Called from within runWithWakeLock.
254      */
runCurrentVibrationWithWakeLockAndDeathLink()255     private void runCurrentVibrationWithWakeLockAndDeathLink() {
256         IBinder vibrationBinderToken = mExecutingConductor.getVibration().callerToken;
257         try {
258             vibrationBinderToken.linkToDeath(mExecutingConductor, 0);
259         } catch (RemoteException e) {
260             Slog.e(TAG, "Error linking vibration to token death", e);
261             clientVibrationCompleteIfNotAlready(
262                     new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_TOKEN));
263             return;
264         }
265         // Ensure that the unlink always occurs now.
266         try {
267             // This is the actual execution of the vibration.
268             playVibration();
269         } finally {
270             try {
271                 vibrationBinderToken.unlinkToDeath(mExecutingConductor, 0);
272             } catch (NoSuchElementException e) {
273                 Slog.wtf(TAG, "Failed to unlink token", e);
274             }
275         }
276     }
277 
278     // Indicate that the vibration is complete. This can be called multiple times only for
279     // convenience of handling error conditions - an error after the client is complete won't
280     // affect the status.
clientVibrationCompleteIfNotAlready(@onNull Vibration.EndInfo vibrationEndInfo)281     private void clientVibrationCompleteIfNotAlready(@NonNull Vibration.EndInfo vibrationEndInfo) {
282         if (!mCalledVibrationCompleteCallback) {
283             mCalledVibrationCompleteCallback = true;
284             mVibratorManagerHooks.onVibrationCompleted(
285                     mExecutingConductor.getVibration().id, vibrationEndInfo);
286         }
287     }
288 
playVibration()289     private void playVibration() {
290         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration");
291         try {
292             mExecutingConductor.prepareToStart();
293             while (!mExecutingConductor.isFinished()) {
294                 boolean readyToRun = mExecutingConductor.waitUntilNextStepIsDue();
295                 // If we waited, don't run the next step, but instead re-evaluate status.
296                 if (readyToRun) {
297                     // Run the step without holding the main lock, to avoid HAL interactions from
298                     // blocking the thread.
299                     mExecutingConductor.runNextStep();
300                 }
301 
302                 if (!mCalledVibrationCompleteCallback) {
303                     // This block can only run once due to mCalledVibrationCompleteCallback.
304                     Vibration.EndInfo vibrationEndInfo =
305                             mExecutingConductor.calculateVibrationEndInfo();
306                     if (vibrationEndInfo != null) {
307                         // First time vibration stopped running, start clean-up tasks and notify
308                         // callback immediately.
309                         clientVibrationCompleteIfNotAlready(vibrationEndInfo);
310                     }
311                 }
312             }
313         } finally {
314             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
315         }
316     }
317 }
318