• 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 static android.os.Trace.TRACE_TAG_VIBRATOR;
20 
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.os.PowerManager;
24 import android.os.Process;
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 import com.android.server.vibrator.VibrationSession.Status;
33 
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     // To enable these logs, run:
40     // 'adb shell setprop persist.log.tag.VibrationThread DEBUG && adb reboot'
41     static final boolean DEBUG = VibratorDebugUtils.isDebuggable(TAG);
42 
43     /** Calls into VibratorManager functionality needed for playing a {@link HalVibration}. */
44     interface VibratorManagerHooks {
45 
46         /**
47          * Request the manager to prepare for triggering a synchronized vibration step.
48          *
49          * @param requiredCapabilities The required syncing capabilities for this preparation step.
50          *                             Expect CAP_SYNC and a combination of values from
51          *                             IVibratorManager.CAP_PREPARE_* and
52          *                             IVibratorManager.CAP_MIXED_TRIGGER_*.
53          * @param vibratorIds          The id of the vibrators to be prepared.
54          */
prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds)55         boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds);
56 
57         /**
58          * Request the manager to trigger a synchronized vibration. The vibration must already
59          * have been prepared with {@link #prepareSyncedVibration}.
60          */
triggerSyncedVibration(long vibrationId)61         boolean triggerSyncedVibration(long vibrationId);
62 
63         /** Tell the manager to cancel a synced vibration. */
cancelSyncedVibration()64         void cancelSyncedVibration();
65 
66         /**
67          * Record that a vibrator was turned on, and may remain on for the specified duration,
68          * on behalf of the given uid.
69          */
noteVibratorOn(int uid, long duration)70         void noteVibratorOn(int uid, long duration);
71 
72         /** Record that a vibrator was turned off, on behalf of the given uid. */
noteVibratorOff(int uid)73         void noteVibratorOff(int uid);
74 
75         /**
76          * Tells the manager that the VibrationThread is finished with the previous vibration and
77          * all of its cleanup tasks, and the vibrators can now be used for another vibration.
78          */
onVibrationThreadReleased(long vibrationId)79         void onVibrationThreadReleased(long vibrationId);
80     }
81 
82     private final PowerManager.WakeLock mWakeLock;
83     private final VibrationThread.VibratorManagerHooks mVibratorManagerHooks;
84 
85     // mLock is used here to communicate that the thread's work status has changed. The
86     // VibrationThread is expected to wait until work arrives, and other threads may wait until
87     // work has finished. Therefore, any changes to the conductor must be followed by a notifyAll
88     // so that threads check if their desired state is achieved.
89     private final Object mLock = new Object();
90 
91     /**
92      * The conductor that is intended to be active. Null value means that a new conductor can
93      * be set to run. Note that this field is only reset to null when mExecutingConductor has
94      * completed, so the two fields should be in sync.
95      */
96     @GuardedBy("mLock")
97     @Nullable
98     private VibrationStepConductor mRequestedActiveConductor;
99 
100     /**
101      * The conductor being executed by this thread, should only be accessed within this thread's
102      * execution. i.e. not thread-safe. {@link #mRequestedActiveConductor} is for cross-thread
103      * signalling.
104      */
105     @Nullable
106     private VibrationStepConductor mExecutingConductor;
107 
108     // Variable only set and read in main thread, no need to lock.
109     private boolean mCalledVibrationCompleteCallback = false;
110 
VibrationThread(PowerManager.WakeLock wakeLock, VibratorManagerHooks vibratorManagerHooks)111     VibrationThread(PowerManager.WakeLock wakeLock, VibratorManagerHooks vibratorManagerHooks) {
112         mWakeLock = wakeLock;
113         mVibratorManagerHooks = vibratorManagerHooks;
114     }
115 
116     /**
117      * Sets/activates the current vibration. Must only be called after receiving
118      * onVibratorsReleased from the previous vibration.
119      *
120      * @return false if VibrationThread couldn't accept it, which shouldn't happen unless called
121      *  before the release callback.
122      */
runVibrationOnVibrationThread(VibrationStepConductor conductor)123     boolean runVibrationOnVibrationThread(VibrationStepConductor conductor) {
124         Trace.traceBegin(TRACE_TAG_VIBRATOR, "runVibrationOnVibrationThread");
125         try {
126             synchronized (mLock) {
127                 if (mRequestedActiveConductor != null) {
128                     Slog.wtf(TAG, "Attempt to start vibration when one already running");
129                     return false;
130                 }
131                 mRequestedActiveConductor = conductor;
132                 mLock.notifyAll();
133             }
134             return true;
135         } finally {
136             Trace.traceEnd(TRACE_TAG_VIBRATOR);
137         }
138     }
139 
140     @Override
run()141     public void run() {
142         Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
143         while (true) {
144             // mExecutingConductor is only modified in this loop.
145             mExecutingConductor = Objects.requireNonNull(waitForVibrationRequest());
146 
147             mCalledVibrationCompleteCallback = false;
148             runCurrentVibrationWithWakeLock();
149             if (!mExecutingConductor.isFinished()) {
150                 Slog.wtf(TAG, "VibrationThread terminated with unfinished vibration");
151             }
152             synchronized (mLock) {
153                 // Allow another vibration to be requested.
154                 mRequestedActiveConductor = null;
155             }
156             // The callback is run without holding the lock, as it may initiate another vibration.
157             // It's safe to notify even if mVibratorConductor has been re-written, as the "wait"
158             // methods all verify their waited state before returning. In reality though, if the
159             // manager is waiting for the thread to finish, then there is no pending vibration
160             // for this thread.
161             // No point doing this in finally, as if there's an exception, this thread will die
162             // and be unusable anyway.
163             mVibratorManagerHooks.onVibrationThreadReleased(
164                     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(
234                 mExecutingConductor.getVibration().callerInfo.uid);
235         mWakeLock.setWorkSource(workSource);
236         mWakeLock.acquire();
237         try {
238             try {
239                 playVibration();
240             } finally {
241                 clientVibrationCompleteIfNotAlready(
242                         new Vibration.EndInfo(Status.FINISHED_UNEXPECTED));
243             }
244         } finally {
245             mWakeLock.release();
246             mWakeLock.setWorkSource(null);
247         }
248     }
249 
250     // Indicate that the vibration is complete. This can be called multiple times only for
251     // convenience of handling error conditions - an error after the client is complete won't
252     // affect the status.
clientVibrationCompleteIfNotAlready(@onNull Vibration.EndInfo vibrationEndInfo)253     private void clientVibrationCompleteIfNotAlready(@NonNull Vibration.EndInfo vibrationEndInfo) {
254         if (!mCalledVibrationCompleteCallback) {
255             mCalledVibrationCompleteCallback = true;
256             Trace.traceBegin(TRACE_TAG_VIBRATOR, "notifyVibrationComplete");
257             try {
258                 mExecutingConductor.notifyVibrationComplete(vibrationEndInfo);
259             } finally {
260                 Trace.traceEnd(TRACE_TAG_VIBRATOR);
261             }
262         }
263     }
264 
playVibration()265     private void playVibration() {
266         Trace.traceBegin(TRACE_TAG_VIBRATOR, "playVibration");
267         try {
268             if (!mExecutingConductor.prepareToStart()) {
269                 // The effect cannot be played, start clean-up tasks and notify
270                 // callback immediately.
271                 clientVibrationCompleteIfNotAlready(
272                         new Vibration.EndInfo(Status.IGNORED_UNSUPPORTED));
273 
274                 return;
275             }
276 
277             while (!mExecutingConductor.isFinished()) {
278                 boolean readyToRun = mExecutingConductor.waitUntilNextStepIsDue();
279                 // If we waited, don't run the next step, but instead re-evaluate status.
280                 if (readyToRun) {
281                     // Run the step without holding the main lock, to avoid HAL interactions from
282                     // blocking the thread.
283                     mExecutingConductor.runNextStep();
284                 }
285 
286                 if (!mCalledVibrationCompleteCallback) {
287                     // This block can only run once due to mCalledVibrationCompleteCallback.
288                     Vibration.EndInfo vibrationEndInfo =
289                             mExecutingConductor.calculateVibrationEndInfo();
290                     if (vibrationEndInfo != null) {
291                         // First time vibration stopped running, start clean-up tasks and notify
292                         // callback immediately.
293                         clientVibrationCompleteIfNotAlready(vibrationEndInfo);
294                     }
295                 }
296             }
297         } finally {
298             Trace.traceEnd(TRACE_TAG_VIBRATOR);
299         }
300     }
301 }
302