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