1 /* 2 * Copyright (C) 2022 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.Build; 22 import android.os.CombinedVibration; 23 import android.os.IBinder; 24 import android.os.VibrationEffect; 25 import android.os.vibrator.PrebakedSegment; 26 import android.os.vibrator.PrimitiveSegment; 27 import android.os.vibrator.RampSegment; 28 import android.os.vibrator.VibrationEffectSegment; 29 import android.util.IntArray; 30 import android.util.Slog; 31 import android.util.SparseArray; 32 33 import com.android.internal.annotations.GuardedBy; 34 35 import java.util.ArrayList; 36 import java.util.Iterator; 37 import java.util.LinkedList; 38 import java.util.List; 39 import java.util.PriorityQueue; 40 import java.util.Queue; 41 42 /** 43 * Creates and manages a queue of steps for performing a VibrationEffect, as well as coordinating 44 * dispatch of callbacks. 45 * 46 * <p>In general, methods in this class are intended to be called only by a single instance of 47 * VibrationThread. The only thread-safe methods for calling from other threads are the "notify" 48 * methods (which should never be used from the VibrationThread thread). 49 */ 50 final class VibrationStepConductor implements IBinder.DeathRecipient { 51 private static final boolean DEBUG = VibrationThread.DEBUG; 52 private static final String TAG = VibrationThread.TAG; 53 54 /** 55 * Extra timeout added to the end of each vibration step to ensure it finishes even when 56 * vibrator callbacks are lost. 57 */ 58 static final long CALLBACKS_EXTRA_TIMEOUT = 1_000; 59 /** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */ 60 static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f; 61 static final List<Step> EMPTY_STEP_LIST = new ArrayList<>(); 62 63 // Used within steps. 64 public final VibrationSettings vibrationSettings; 65 public final DeviceVibrationEffectAdapter deviceEffectAdapter; 66 public final VibrationThread.VibratorManagerHooks vibratorManagerHooks; 67 68 // Not guarded by lock because they're not modified by this conductor, it's used here only to 69 // check immutable attributes. The status and other mutable states are changed by the service or 70 // by the vibrator steps. 71 private final Vibration mVibration; 72 private final SparseArray<VibratorController> mVibrators = new SparseArray<>(); 73 74 private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>(); 75 private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>(); 76 77 // Signalling fields. 78 // Note that vibrator callback signals may happen inside vibrator HAL calls made by the 79 // VibrationThread, or on an external executor, so this lock should not be held for anything 80 // other than updating signalling state - particularly not during HAL calls or when invoking 81 // other callbacks that may trigger calls into the thread. 82 private final Object mLock = new Object(); 83 @GuardedBy("mLock") 84 private final IntArray mSignalVibratorsComplete; 85 @Nullable 86 @GuardedBy("mLock") 87 private Vibration.EndInfo mSignalCancel = null; 88 @GuardedBy("mLock") 89 private boolean mSignalCancelImmediate = false; 90 91 @Nullable 92 private Vibration.EndInfo mCancelledVibrationEndInfo = null; 93 private boolean mCancelledImmediately = false; // hard stop 94 private int mPendingVibrateSteps; 95 private int mRemainingStartSequentialEffectSteps; 96 private int mSuccessfulVibratorOnSteps; 97 VibrationStepConductor(Vibration vib, VibrationSettings vibrationSettings, DeviceVibrationEffectAdapter effectAdapter, SparseArray<VibratorController> availableVibrators, VibrationThread.VibratorManagerHooks vibratorManagerHooks)98 VibrationStepConductor(Vibration vib, VibrationSettings vibrationSettings, 99 DeviceVibrationEffectAdapter effectAdapter, 100 SparseArray<VibratorController> availableVibrators, 101 VibrationThread.VibratorManagerHooks vibratorManagerHooks) { 102 this.mVibration = vib; 103 this.vibrationSettings = vibrationSettings; 104 this.deviceEffectAdapter = effectAdapter; 105 this.vibratorManagerHooks = vibratorManagerHooks; 106 107 CombinedVibration effect = vib.getEffect(); 108 for (int i = 0; i < availableVibrators.size(); i++) { 109 if (effect.hasVibrator(availableVibrators.keyAt(i))) { 110 mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i)); 111 } 112 } 113 this.mSignalVibratorsComplete = new IntArray(mVibrators.size()); 114 } 115 116 @Nullable nextVibrateStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int segmentIndex, long previousStepVibratorOffTimeout)117 AbstractVibratorStep nextVibrateStep(long startTime, VibratorController controller, 118 VibrationEffect.Composed effect, int segmentIndex, 119 long previousStepVibratorOffTimeout) { 120 if (Build.IS_DEBUGGABLE) { 121 expectIsVibrationThread(true); 122 } 123 if (segmentIndex >= effect.getSegments().size()) { 124 segmentIndex = effect.getRepeatIndex(); 125 } 126 if (segmentIndex < 0) { 127 // No more segments to play, last step is to complete the vibration on this vibrator. 128 return new CompleteEffectVibratorStep(this, startTime, /* cancelled= */ false, 129 controller, previousStepVibratorOffTimeout); 130 } 131 132 VibrationEffectSegment segment = effect.getSegments().get(segmentIndex); 133 if (segment instanceof PrebakedSegment) { 134 return new PerformPrebakedVibratorStep(this, startTime, controller, effect, 135 segmentIndex, previousStepVibratorOffTimeout); 136 } 137 if (segment instanceof PrimitiveSegment) { 138 return new ComposePrimitivesVibratorStep(this, startTime, controller, effect, 139 segmentIndex, previousStepVibratorOffTimeout); 140 } 141 if (segment instanceof RampSegment) { 142 return new ComposePwleVibratorStep(this, startTime, controller, effect, segmentIndex, 143 previousStepVibratorOffTimeout); 144 } 145 return new SetAmplitudeVibratorStep(this, startTime, controller, effect, segmentIndex, 146 previousStepVibratorOffTimeout); 147 } 148 149 /** Called when this conductor is going to be started running by the VibrationThread. */ prepareToStart()150 public void prepareToStart() { 151 if (Build.IS_DEBUGGABLE) { 152 expectIsVibrationThread(true); 153 } 154 CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect()); 155 mPendingVibrateSteps++; 156 // This count is decremented at the completion of the step, so we don't subtract one. 157 mRemainingStartSequentialEffectSteps = sequentialEffect.getEffects().size(); 158 mNextSteps.offer(new StartSequentialEffectStep(this, sequentialEffect)); 159 // Vibration will start playing in the Vibrator, following the effect timings and delays. 160 // Report current time as the vibration start time, for debugging. 161 mVibration.stats().reportStarted(); 162 } 163 getVibration()164 public Vibration getVibration() { 165 // No thread assertion: immutable 166 return mVibration; 167 } 168 getVibrators()169 SparseArray<VibratorController> getVibrators() { 170 // No thread assertion: immutable 171 return mVibrators; 172 } 173 isFinished()174 public boolean isFinished() { 175 if (Build.IS_DEBUGGABLE) { 176 expectIsVibrationThread(true); 177 } 178 if (mCancelledImmediately) { 179 return true; // Terminate. 180 } 181 182 // No need to check for vibration complete callbacks - if there were any, they would 183 // have no steps to notify anyway. 184 return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty(); 185 } 186 187 /** 188 * Calculate the {@link Vibration.Status} based on the current queue state and the expected 189 * number of {@link StartSequentialEffectStep} to be played. 190 */ 191 @Nullable calculateVibrationEndInfo()192 public Vibration.EndInfo calculateVibrationEndInfo() { 193 if (Build.IS_DEBUGGABLE) { 194 expectIsVibrationThread(true); 195 } 196 197 if (mCancelledVibrationEndInfo != null) { 198 return mCancelledVibrationEndInfo; 199 } 200 if (mPendingVibrateSteps > 0 || mRemainingStartSequentialEffectSteps > 0) { 201 // Vibration still running. 202 return null; 203 } 204 // No pending steps, and something happened. 205 if (mSuccessfulVibratorOnSteps > 0) { 206 return new Vibration.EndInfo(Vibration.Status.FINISHED); 207 } 208 // If no step was able to turn the vibrator ON successfully. 209 return new Vibration.EndInfo(Vibration.Status.IGNORED_UNSUPPORTED); 210 } 211 212 /** 213 * Blocks until the next step is due to run. The wait here may be interrupted by calling 214 * one of the "notify" methods. 215 * 216 * <p>This method returns true if the next step is ready to run now. If the method returns 217 * false, then some waiting was done, but may have been interrupted by a wakeUp, and the 218 * status and isFinished of the vibration should be re-checked before calling this method again. 219 * 220 * @return true if the next step can be run now or the vibration is finished, or false if this 221 * method waited and the conductor state may have changed asynchronously, in which case this 222 * method needs to be run again. 223 */ waitUntilNextStepIsDue()224 public boolean waitUntilNextStepIsDue() { 225 if (Build.IS_DEBUGGABLE) { 226 expectIsVibrationThread(true); 227 } 228 229 processAllNotifySignals(); 230 if (mCancelledImmediately) { 231 // Don't try to run a step for immediate cancel, although there should be none left. 232 // Non-immediate cancellation may have cleanup steps, so it continues processing. 233 return false; 234 } 235 if (!mPendingOnVibratorCompleteSteps.isEmpty()) { 236 return true; // Resumed step ready. 237 } 238 Step nextStep = mNextSteps.peek(); 239 if (nextStep == null) { 240 return true; // Finished 241 } 242 long waitMillis = nextStep.calculateWaitTime(); 243 if (waitMillis <= 0) { 244 return true; // Regular step ready 245 } 246 synchronized (mLock) { 247 // Double check for signals before sleeping, as their notify wouldn't interrupt a fresh 248 // wait. 249 if (hasPendingNotifySignalLocked()) { 250 // Don't run the next step, it will loop back to this method and process them. 251 return false; 252 } 253 try { 254 mLock.wait(waitMillis); 255 } catch (InterruptedException e) { 256 } 257 return false; // Caller needs to check isFinished and maybe wait again. 258 } 259 } 260 261 @Nullable pollNext()262 private Step pollNext() { 263 if (Build.IS_DEBUGGABLE) { 264 expectIsVibrationThread(true); 265 } 266 267 // Prioritize the steps resumed by a vibrator complete callback, irrespective of their 268 // "next run time". 269 if (!mPendingOnVibratorCompleteSteps.isEmpty()) { 270 return mPendingOnVibratorCompleteSteps.poll(); 271 } 272 return mNextSteps.poll(); 273 } 274 275 /** 276 * Play and remove the step at the top of this queue, and also adds the next steps generated 277 * to be played next. 278 */ runNextStep()279 public void runNextStep() { 280 if (Build.IS_DEBUGGABLE) { 281 expectIsVibrationThread(true); 282 } 283 // In theory a completion callback could have come in between the wait finishing and 284 // this method starting, but that only means the step is due now anyway, so it's reasonable 285 // to run it before processing callbacks as the window is tiny. 286 Step nextStep = pollNext(); 287 if (nextStep != null) { 288 List<Step> nextSteps = nextStep.play(); 289 if (nextStep.getVibratorOnDuration() > 0) { 290 mSuccessfulVibratorOnSteps++; 291 } 292 if (nextStep instanceof StartSequentialEffectStep) { 293 mRemainingStartSequentialEffectSteps--; 294 } 295 if (!nextStep.isCleanUp()) { 296 mPendingVibrateSteps--; 297 } 298 for (int i = 0; i < nextSteps.size(); i++) { 299 mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1; 300 } 301 mNextSteps.addAll(nextSteps); 302 } 303 } 304 305 /** 306 * Binder death notification. VibrationThread registers this when it's running a conductor. 307 * Note that cancellation could theoretically happen immediately, before the conductor has 308 * started, but in this case it will be processed in the first signals loop. 309 */ 310 @Override binderDied()311 public void binderDied() { 312 if (DEBUG) { 313 Slog.d(TAG, "Binder died, cancelling vibration..."); 314 } 315 notifyCancelled(new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED), 316 /* immediate= */ false); 317 } 318 319 /** 320 * Notify the execution that cancellation is requested. This will be acted upon 321 * asynchronously in the VibrationThread. 322 * 323 * <p>Only the first cancel signal will be used to end a cancelled vibration, but subsequent 324 * calls with {@code immediate} flag set to true can still force the first cancel signal to 325 * take effect urgently. 326 * 327 * @param immediate indicates whether cancellation should abort urgently and skip cleanup steps. 328 */ notifyCancelled(@onNull Vibration.EndInfo cancelInfo, boolean immediate)329 public void notifyCancelled(@NonNull Vibration.EndInfo cancelInfo, boolean immediate) { 330 if (Build.IS_DEBUGGABLE) { 331 expectIsVibrationThread(false); 332 } 333 if (DEBUG) { 334 Slog.d(TAG, "Vibration cancel requested with signal=" + cancelInfo 335 + ", immediate=" + immediate); 336 } 337 if ((cancelInfo == null) || !cancelInfo.status.name().startsWith("CANCEL")) { 338 Slog.w(TAG, "Vibration cancel requested with bad signal=" + cancelInfo 339 + ", using CANCELLED_UNKNOWN_REASON to ensure cancellation."); 340 cancelInfo = new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_UNKNOWN_REASON); 341 } 342 synchronized (mLock) { 343 if ((immediate && mSignalCancelImmediate) || (mSignalCancel != null)) { 344 if (DEBUG) { 345 Slog.d(TAG, "Vibration cancel request ignored as the vibration " 346 + mVibration.id + "is already being cancelled with signal=" 347 + mSignalCancel + ", immediate=" + mSignalCancelImmediate); 348 } 349 return; 350 } 351 mSignalCancelImmediate |= immediate; 352 if (mSignalCancel == null) { 353 mSignalCancel = cancelInfo; 354 } else { 355 if (DEBUG) { 356 Slog.d(TAG, "Vibration cancel request new signal=" + cancelInfo 357 + " ignored as the vibration was already cancelled with signal=" 358 + mSignalCancel + ", but immediate flag was updated to " 359 + mSignalCancelImmediate); 360 } 361 } 362 mLock.notify(); 363 } 364 } 365 366 /** 367 * Notify the conductor that a vibrator has completed its work. 368 * 369 * <p>This is a lightweight method intended to be called directly via native callbacks. 370 * The state update is recorded for processing on the main execution thread (VibrationThread). 371 */ notifyVibratorComplete(int vibratorId)372 public void notifyVibratorComplete(int vibratorId) { 373 // HAL callbacks may be triggered directly within HAL calls, so these notifications 374 // could be on the VibrationThread as it calls the HAL, or some other executor later. 375 // Therefore no thread assertion is made here. 376 377 if (DEBUG) { 378 Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId); 379 } 380 381 synchronized (mLock) { 382 mSignalVibratorsComplete.add(vibratorId); 383 mLock.notify(); 384 } 385 } 386 387 /** 388 * Notify that a VibratorManager sync operation has completed. 389 * 390 * <p>This is a lightweight method intended to be called directly via native callbacks. 391 * The state update is recorded for processing on the main execution thread 392 * (VibrationThread). 393 */ notifySyncedVibrationComplete()394 public void notifySyncedVibrationComplete() { 395 // HAL callbacks may be triggered directly within HAL calls, so these notifications 396 // could be on the VibrationThread as it calls the HAL, or some other executor later. 397 // Therefore no thread assertion is made here. 398 399 if (DEBUG) { 400 Slog.d(TAG, "Synced vibration complete reported by vibrator manager"); 401 } 402 403 synchronized (mLock) { 404 for (int i = 0; i < mVibrators.size(); i++) { 405 mSignalVibratorsComplete.add(mVibrators.keyAt(i)); 406 } 407 mLock.notify(); 408 } 409 } 410 411 /** Returns true if a cancellation signal was sent via {@link #notifyCancelled}. */ wasNotifiedToCancel()412 public boolean wasNotifiedToCancel() { 413 if (Build.IS_DEBUGGABLE) { 414 expectIsVibrationThread(false); 415 } 416 synchronized (mLock) { 417 return mSignalCancel != null; 418 } 419 } 420 421 @GuardedBy("mLock") hasPendingNotifySignalLocked()422 private boolean hasPendingNotifySignalLocked() { 423 if (Build.IS_DEBUGGABLE) { 424 expectIsVibrationThread(true); // Reads VibrationThread variables as well as signals. 425 } 426 return (mSignalCancel != null && mCancelledVibrationEndInfo == null) 427 || (mSignalCancelImmediate && !mCancelledImmediately) 428 || (mSignalVibratorsComplete.size() > 0); 429 } 430 431 /** 432 * Process any notified cross-thread signals, applying the necessary VibrationThread state 433 * changes. 434 */ processAllNotifySignals()435 private void processAllNotifySignals() { 436 if (Build.IS_DEBUGGABLE) { 437 expectIsVibrationThread(true); 438 } 439 440 int[] vibratorsToProcess = null; 441 Vibration.EndInfo doCancelInfo = null; 442 boolean doCancelImmediate = false; 443 // Collect signals to process, but don't keep the lock while processing them. 444 synchronized (mLock) { 445 if (mSignalCancelImmediate) { 446 if (mCancelledImmediately) { 447 Slog.wtf(TAG, "Immediate cancellation signal processed twice"); 448 } 449 // This should only happen once. 450 doCancelImmediate = true; 451 doCancelInfo = mSignalCancel; 452 } 453 if ((mSignalCancel != null) && (mCancelledVibrationEndInfo == null)) { 454 doCancelInfo = mSignalCancel; 455 } 456 if (!doCancelImmediate && mSignalVibratorsComplete.size() > 0) { 457 // Swap out the queue of completions to process. 458 vibratorsToProcess = mSignalVibratorsComplete.toArray(); // makes a copy 459 mSignalVibratorsComplete.clear(); 460 } 461 } 462 463 // Force cancellation means stop everything and clear all steps, so the execution loop 464 // shouldn't come back to this method. To observe explicitly: this drops vibrator 465 // completion signals that were collected in this call, but we won't process them 466 // anyway as all steps are cancelled. 467 if (doCancelImmediate) { 468 processCancelImmediately(doCancelInfo); 469 return; 470 } 471 if (doCancelInfo != null) { 472 processCancel(doCancelInfo); 473 } 474 if (vibratorsToProcess != null) { 475 processVibratorsComplete(vibratorsToProcess); 476 } 477 } 478 479 /** 480 * Cancel the current queue, replacing all remaining steps with respective clean-up steps. 481 * 482 * <p>This will remove all steps and replace them with respective results of 483 * {@link Step#cancel()}. 484 */ processCancel(Vibration.EndInfo cancelInfo)485 public void processCancel(Vibration.EndInfo cancelInfo) { 486 if (Build.IS_DEBUGGABLE) { 487 expectIsVibrationThread(true); 488 } 489 490 mCancelledVibrationEndInfo = cancelInfo; 491 // Vibrator callbacks should wait until all steps from the queue are properly cancelled 492 // and clean up steps are added back to the queue, so they can handle the callback. 493 List<Step> cleanUpSteps = new ArrayList<>(); 494 Step step; 495 while ((step = pollNext()) != null) { 496 cleanUpSteps.addAll(step.cancel()); 497 } 498 // All steps generated by Step.cancel() should be clean-up steps. 499 mPendingVibrateSteps = 0; 500 mNextSteps.addAll(cleanUpSteps); 501 } 502 503 /** 504 * Cancel the current queue immediately, clearing all remaining steps and skipping clean-up. 505 * 506 * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order. 507 */ processCancelImmediately(Vibration.EndInfo cancelInfo)508 public void processCancelImmediately(Vibration.EndInfo cancelInfo) { 509 if (Build.IS_DEBUGGABLE) { 510 expectIsVibrationThread(true); 511 } 512 513 mCancelledImmediately = true; 514 mCancelledVibrationEndInfo = cancelInfo; 515 Step step; 516 while ((step = pollNext()) != null) { 517 step.cancelImmediately(); 518 } 519 mPendingVibrateSteps = 0; 520 } 521 522 /** 523 * Processes the vibrators that have sent their complete callbacks. A step is found that will 524 * accept the completion callback, and this step is brought forward for execution in the next 525 * run. 526 * 527 * <p>This assumes only one of the next steps is waiting on this given vibrator, so the 528 * first step found will be resumed by this method, in no particular order. 529 */ processVibratorsComplete(@onNull int[] vibratorsToProcess)530 private void processVibratorsComplete(@NonNull int[] vibratorsToProcess) { 531 if (Build.IS_DEBUGGABLE) { 532 expectIsVibrationThread(true); 533 } 534 535 for (int vibratorId : vibratorsToProcess) { 536 Iterator<Step> it = mNextSteps.iterator(); 537 while (it.hasNext()) { 538 Step step = it.next(); 539 if (step.acceptVibratorCompleteCallback(vibratorId)) { 540 it.remove(); 541 mPendingOnVibratorCompleteSteps.offer(step); 542 break; 543 } 544 } 545 } 546 } 547 toSequential(CombinedVibration effect)548 private static CombinedVibration.Sequential toSequential(CombinedVibration effect) { 549 if (effect instanceof CombinedVibration.Sequential) { 550 return (CombinedVibration.Sequential) effect; 551 } 552 return (CombinedVibration.Sequential) CombinedVibration.startSequential() 553 .addNext(effect) 554 .combine(); 555 } 556 557 /** 558 * This check is used for debugging and documentation to indicate the thread that's expected 559 * to invoke a given public method on this class. Most methods are only invoked by 560 * VibrationThread, which is where all the steps and HAL calls should be made. Other threads 561 * should only signal to the execution flow being run by VibrationThread. 562 */ expectIsVibrationThread(boolean isVibrationThread)563 private static void expectIsVibrationThread(boolean isVibrationThread) { 564 if ((Thread.currentThread() instanceof VibrationThread) != isVibrationThread) { 565 Slog.wtfStack("VibrationStepConductor", 566 "Thread caller assertion failed, expected isVibrationThread=" 567 + isVibrationThread); 568 } 569 } 570 } 571