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