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