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