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.hardware.vibrator.IVibratorManager; 22 import android.os.CombinedVibration; 23 import android.os.IBinder; 24 import android.os.PowerManager; 25 import android.os.Process; 26 import android.os.RemoteException; 27 import android.os.SystemClock; 28 import android.os.Trace; 29 import android.os.VibrationEffect; 30 import android.os.VibratorInfo; 31 import android.os.WorkSource; 32 import android.os.vibrator.PrebakedSegment; 33 import android.os.vibrator.PrimitiveSegment; 34 import android.os.vibrator.RampSegment; 35 import android.os.vibrator.StepSegment; 36 import android.os.vibrator.VibrationEffectSegment; 37 import android.util.Slog; 38 import android.util.SparseArray; 39 40 import com.android.internal.annotations.GuardedBy; 41 import com.android.internal.annotations.VisibleForTesting; 42 import com.android.internal.app.IBatteryStats; 43 import com.android.internal.util.FrameworkStatsLog; 44 45 import java.util.ArrayList; 46 import java.util.Arrays; 47 import java.util.Iterator; 48 import java.util.LinkedList; 49 import java.util.List; 50 import java.util.NoSuchElementException; 51 import java.util.PriorityQueue; 52 import java.util.Queue; 53 54 /** Plays a {@link Vibration} in dedicated thread. */ 55 final class VibrationThread extends Thread implements IBinder.DeathRecipient { 56 private static final String TAG = "VibrationThread"; 57 private static final boolean DEBUG = false; 58 59 /** 60 * Extra timeout added to the end of each vibration step to ensure it finishes even when 61 * vibrator callbacks are lost. 62 */ 63 private static final long CALLBACKS_EXTRA_TIMEOUT = 1_000; 64 65 /** Threshold to prevent the ramp off steps from trying to set extremely low amplitudes. */ 66 private static final float RAMP_OFF_AMPLITUDE_MIN = 1e-3f; 67 68 /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */ 69 private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000; 70 71 private static final List<Step> EMPTY_STEP_LIST = new ArrayList<>(); 72 73 /** Callbacks for playing a {@link Vibration}. */ 74 interface VibrationCallbacks { 75 76 /** 77 * Callback triggered before starting a synchronized vibration step. This will be called 78 * with {@code requiredCapabilities = 0} if no synchronization is required. 79 * 80 * @param requiredCapabilities The required syncing capabilities for this preparation step. 81 * Expects a combination of values from 82 * IVibratorManager.CAP_PREPARE_* and 83 * IVibratorManager.CAP_MIXED_TRIGGER_*. 84 * @param vibratorIds The id of the vibrators to be prepared. 85 */ prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds)86 boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds); 87 88 /** Callback triggered after synchronized vibrations were prepared. */ triggerSyncedVibration(long vibrationId)89 boolean triggerSyncedVibration(long vibrationId); 90 91 /** Callback triggered to cancel a prepared synced vibration. */ cancelSyncedVibration()92 void cancelSyncedVibration(); 93 94 /** Callback triggered when the vibration is complete. */ onVibrationCompleted(long vibrationId, Vibration.Status status)95 void onVibrationCompleted(long vibrationId, Vibration.Status status); 96 97 /** Callback triggered when the vibrators are released after the thread is complete. */ onVibratorsReleased()98 void onVibratorsReleased(); 99 } 100 101 private final Object mLock = new Object(); 102 private final WorkSource mWorkSource; 103 private final PowerManager.WakeLock mWakeLock; 104 private final IBatteryStats mBatteryStatsService; 105 private final VibrationSettings mVibrationSettings; 106 private final DeviceVibrationEffectAdapter mDeviceEffectAdapter; 107 private final Vibration mVibration; 108 private final VibrationCallbacks mCallbacks; 109 private final SparseArray<VibratorController> mVibrators = new SparseArray<>(); 110 private final StepQueue mStepQueue = new StepQueue(); 111 112 private volatile boolean mStop; 113 private volatile boolean mForceStop; 114 // Variable only set and read in main thread. 115 private boolean mCalledVibrationCompleteCallback = false; 116 VibrationThread(Vibration vib, VibrationSettings vibrationSettings, DeviceVibrationEffectAdapter effectAdapter, SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock, IBatteryStats batteryStatsService, VibrationCallbacks callbacks)117 VibrationThread(Vibration vib, VibrationSettings vibrationSettings, 118 DeviceVibrationEffectAdapter effectAdapter, 119 SparseArray<VibratorController> availableVibrators, PowerManager.WakeLock wakeLock, 120 IBatteryStats batteryStatsService, VibrationCallbacks callbacks) { 121 mVibration = vib; 122 mVibrationSettings = vibrationSettings; 123 mDeviceEffectAdapter = effectAdapter; 124 mCallbacks = callbacks; 125 mWorkSource = new WorkSource(mVibration.uid); 126 mWakeLock = wakeLock; 127 mBatteryStatsService = batteryStatsService; 128 129 CombinedVibration effect = vib.getEffect(); 130 for (int i = 0; i < availableVibrators.size(); i++) { 131 if (effect.hasVibrator(availableVibrators.keyAt(i))) { 132 mVibrators.put(availableVibrators.keyAt(i), availableVibrators.valueAt(i)); 133 } 134 } 135 } 136 getVibration()137 Vibration getVibration() { 138 return mVibration; 139 } 140 141 @VisibleForTesting getVibrators()142 SparseArray<VibratorController> getVibrators() { 143 return mVibrators; 144 } 145 146 @Override binderDied()147 public void binderDied() { 148 if (DEBUG) { 149 Slog.d(TAG, "Binder died, cancelling vibration..."); 150 } 151 cancel(); 152 } 153 154 @Override run()155 public void run() { 156 // Structured to guarantee the vibrators completed and released callbacks at the end of 157 // thread execution. Both of these callbacks are exclusively called from this thread. 158 try { 159 try { 160 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); 161 runWithWakeLock(); 162 } finally { 163 clientVibrationCompleteIfNotAlready(Vibration.Status.FINISHED_UNEXPECTED); 164 } 165 } finally { 166 mCallbacks.onVibratorsReleased(); 167 } 168 } 169 170 /** Runs the VibrationThread ensuring that the wake lock is acquired and released. */ runWithWakeLock()171 private void runWithWakeLock() { 172 mWakeLock.setWorkSource(mWorkSource); 173 mWakeLock.acquire(); 174 try { 175 runWithWakeLockAndDeathLink(); 176 } finally { 177 mWakeLock.release(); 178 } 179 } 180 181 /** 182 * Runs the VibrationThread with the binder death link, handling link/unlink failures. 183 * Called from within runWithWakeLock. 184 */ runWithWakeLockAndDeathLink()185 private void runWithWakeLockAndDeathLink() { 186 try { 187 mVibration.token.linkToDeath(this, 0); 188 } catch (RemoteException e) { 189 Slog.e(TAG, "Error linking vibration to token death", e); 190 clientVibrationCompleteIfNotAlready(Vibration.Status.IGNORED_ERROR_TOKEN); 191 return; 192 } 193 // Ensure that the unlink always occurs now. 194 try { 195 // This is the actual execution of the vibration. 196 playVibration(); 197 } finally { 198 try { 199 mVibration.token.unlinkToDeath(this, 0); 200 } catch (NoSuchElementException e) { 201 Slog.wtf(TAG, "Failed to unlink token", e); 202 } 203 } 204 } 205 206 /** Cancel current vibration and ramp down the vibrators gracefully. */ cancel()207 public void cancel() { 208 if (mStop) { 209 // Already cancelled, running clean-up steps. 210 return; 211 } 212 mStop = true; 213 synchronized (mLock) { 214 if (DEBUG) { 215 Slog.d(TAG, "Vibration cancelled"); 216 } 217 mLock.notify(); 218 } 219 } 220 221 /** Cancel current vibration and shuts off the vibrators immediately. */ cancelImmediately()222 public void cancelImmediately() { 223 if (mForceStop) { 224 // Already forced the thread to stop, wait for it to finish. 225 return; 226 } 227 mStop = mForceStop = true; 228 synchronized (mLock) { 229 if (DEBUG) { 230 Slog.d(TAG, "Vibration cancelled immediately"); 231 } 232 mLock.notify(); 233 } 234 } 235 236 /** Notify current vibration that a synced step has completed. */ syncedVibrationComplete()237 public void syncedVibrationComplete() { 238 synchronized (mLock) { 239 if (DEBUG) { 240 Slog.d(TAG, "Synced vibration complete reported by vibrator manager"); 241 } 242 for (int i = 0; i < mVibrators.size(); i++) { 243 mStepQueue.notifyVibratorComplete(mVibrators.keyAt(i)); 244 } 245 mLock.notify(); 246 } 247 } 248 249 /** Notify current vibration that a step has completed on given vibrator. */ vibratorComplete(int vibratorId)250 public void vibratorComplete(int vibratorId) { 251 synchronized (mLock) { 252 if (DEBUG) { 253 Slog.d(TAG, "Vibration complete reported by vibrator " + vibratorId); 254 } 255 mStepQueue.notifyVibratorComplete(vibratorId); 256 mLock.notify(); 257 } 258 } 259 260 // Indicate that the vibration is complete. This can be called multiple times only for 261 // convenience of handling error conditions - an error after the client is complete won't 262 // affect the status. clientVibrationCompleteIfNotAlready(Vibration.Status completedStatus)263 private void clientVibrationCompleteIfNotAlready(Vibration.Status completedStatus) { 264 if (!mCalledVibrationCompleteCallback) { 265 mCalledVibrationCompleteCallback = true; 266 mCallbacks.onVibrationCompleted(mVibration.id, completedStatus); 267 } 268 } 269 playVibration()270 private void playVibration() { 271 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playVibration"); 272 try { 273 CombinedVibration.Sequential sequentialEffect = toSequential(mVibration.getEffect()); 274 final int sequentialEffectSize = sequentialEffect.getEffects().size(); 275 mStepQueue.offer(new StartVibrateStep(sequentialEffect)); 276 277 while (!mStepQueue.isEmpty()) { 278 long waitTime; 279 synchronized (mLock) { 280 waitTime = mStepQueue.calculateWaitTime(); 281 if (waitTime > 0) { 282 try { 283 mLock.wait(waitTime); 284 } catch (InterruptedException e) { 285 } 286 } 287 } 288 // If we waited, the queue may have changed, so let the loop run again. 289 if (waitTime <= 0) { 290 mStepQueue.consumeNext(); 291 } 292 Vibration.Status status = mStop ? Vibration.Status.CANCELLED 293 : mStepQueue.calculateVibrationStatus(sequentialEffectSize); 294 if (status != Vibration.Status.RUNNING && !mCalledVibrationCompleteCallback) { 295 // First time vibration stopped running, start clean-up tasks and notify 296 // callback immediately. 297 clientVibrationCompleteIfNotAlready(status); 298 if (status == Vibration.Status.CANCELLED) { 299 mStepQueue.cancel(); 300 } 301 } 302 if (mForceStop) { 303 // Cancel every step and stop playing them right away, even clean-up steps. 304 mStepQueue.cancelImmediately(); 305 clientVibrationCompleteIfNotAlready(Vibration.Status.CANCELLED); 306 break; 307 } 308 } 309 } finally { 310 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 311 } 312 } 313 noteVibratorOn(long duration)314 private void noteVibratorOn(long duration) { 315 try { 316 if (duration <= 0) { 317 return; 318 } 319 if (duration == Long.MAX_VALUE) { 320 // Repeating duration has started. Report a fixed duration here, noteVibratorOff 321 // should be called when this is cancelled. 322 duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION; 323 } 324 mBatteryStatsService.noteVibratorOn(mVibration.uid, duration); 325 FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, 326 mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, 327 duration); 328 } catch (RemoteException e) { 329 } 330 } 331 noteVibratorOff()332 private void noteVibratorOff() { 333 try { 334 mBatteryStatsService.noteVibratorOff(mVibration.uid); 335 FrameworkStatsLog.write_non_chained(FrameworkStatsLog.VIBRATOR_STATE_CHANGED, 336 mVibration.uid, null, FrameworkStatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, 337 /* duration= */ 0); 338 } catch (RemoteException e) { 339 } 340 } 341 342 @Nullable nextVibrateStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int segmentIndex, long vibratorOffTimeout)343 private SingleVibratorStep nextVibrateStep(long startTime, VibratorController controller, 344 VibrationEffect.Composed effect, int segmentIndex, long vibratorOffTimeout) { 345 if (segmentIndex >= effect.getSegments().size()) { 346 segmentIndex = effect.getRepeatIndex(); 347 } 348 if (segmentIndex < 0) { 349 // No more segments to play, last step is to complete the vibration on this vibrator. 350 return new CompleteStep(startTime, /* cancelled= */ false, controller, 351 vibratorOffTimeout); 352 } 353 354 VibrationEffectSegment segment = effect.getSegments().get(segmentIndex); 355 if (segment instanceof PrebakedSegment) { 356 return new PerformStep(startTime, controller, effect, segmentIndex, vibratorOffTimeout); 357 } 358 if (segment instanceof PrimitiveSegment) { 359 return new ComposePrimitivesStep(startTime, controller, effect, segmentIndex, 360 vibratorOffTimeout); 361 } 362 if (segment instanceof RampSegment) { 363 return new ComposePwleStep(startTime, controller, effect, segmentIndex, 364 vibratorOffTimeout); 365 } 366 return new AmplitudeStep(startTime, controller, effect, segmentIndex, vibratorOffTimeout); 367 } 368 toSequential(CombinedVibration effect)369 private static CombinedVibration.Sequential toSequential(CombinedVibration effect) { 370 if (effect instanceof CombinedVibration.Sequential) { 371 return (CombinedVibration.Sequential) effect; 372 } 373 return (CombinedVibration.Sequential) CombinedVibration.startSequential() 374 .addNext(effect) 375 .combine(); 376 } 377 378 /** Queue for {@link Step Steps}, sorted by their start time. */ 379 private final class StepQueue { 380 @GuardedBy("mLock") 381 private final PriorityQueue<Step> mNextSteps = new PriorityQueue<>(); 382 @GuardedBy("mLock") 383 private final Queue<Step> mPendingOnVibratorCompleteSteps = new LinkedList<>(); 384 @GuardedBy("mLock") 385 private final Queue<Integer> mNotifiedVibrators = new LinkedList<>(); 386 387 @GuardedBy("mLock") 388 private int mPendingVibrateSteps; 389 @GuardedBy("mLock") 390 private int mConsumedStartVibrateSteps; 391 @GuardedBy("mLock") 392 private int mSuccessfulVibratorOnSteps; 393 @GuardedBy("mLock") 394 private boolean mWaitToProcessVibratorCallbacks; 395 offer(@onNull Step step)396 public void offer(@NonNull Step step) { 397 synchronized (mLock) { 398 if (!step.isCleanUp()) { 399 mPendingVibrateSteps++; 400 } 401 mNextSteps.offer(step); 402 } 403 } 404 isEmpty()405 public boolean isEmpty() { 406 synchronized (mLock) { 407 return mPendingOnVibratorCompleteSteps.isEmpty() && mNextSteps.isEmpty(); 408 } 409 } 410 411 /** 412 * Calculate the {@link Vibration.Status} based on the current queue state and the expected 413 * number of {@link StartVibrateStep} to be played. 414 */ calculateVibrationStatus(int expectedStartVibrateSteps)415 public Vibration.Status calculateVibrationStatus(int expectedStartVibrateSteps) { 416 synchronized (mLock) { 417 if (mPendingVibrateSteps > 0 418 || mConsumedStartVibrateSteps < expectedStartVibrateSteps) { 419 return Vibration.Status.RUNNING; 420 } 421 if (mSuccessfulVibratorOnSteps > 0) { 422 return Vibration.Status.FINISHED; 423 } 424 // If no step was able to turn the vibrator ON successfully. 425 return Vibration.Status.IGNORED_UNSUPPORTED; 426 } 427 } 428 429 /** Returns the time in millis to wait before calling {@link #consumeNext()}. */ 430 @GuardedBy("mLock") calculateWaitTime()431 public long calculateWaitTime() { 432 if (!mPendingOnVibratorCompleteSteps.isEmpty()) { 433 // Steps anticipated by vibrator complete callback should be played right away. 434 return 0; 435 } 436 Step nextStep = mNextSteps.peek(); 437 return nextStep == null ? 0 : nextStep.calculateWaitTime(); 438 } 439 440 /** 441 * Play and remove the step at the top of this queue, and also adds the next steps generated 442 * to be played next. 443 */ consumeNext()444 public void consumeNext() { 445 // Vibrator callbacks should wait until the polled step is played and the next steps are 446 // added back to the queue, so they can handle the callback. 447 markWaitToProcessVibratorCallbacks(); 448 try { 449 Step nextStep = pollNext(); 450 if (nextStep != null) { 451 // This might turn on the vibrator and have a HAL latency. Execute this outside 452 // any lock to avoid blocking other interactions with the thread. 453 List<Step> nextSteps = nextStep.play(); 454 synchronized (mLock) { 455 if (nextStep.getVibratorOnDuration() > 0) { 456 mSuccessfulVibratorOnSteps++; 457 } 458 if (nextStep instanceof StartVibrateStep) { 459 mConsumedStartVibrateSteps++; 460 } 461 if (!nextStep.isCleanUp()) { 462 mPendingVibrateSteps--; 463 } 464 for (int i = 0; i < nextSteps.size(); i++) { 465 mPendingVibrateSteps += nextSteps.get(i).isCleanUp() ? 0 : 1; 466 } 467 mNextSteps.addAll(nextSteps); 468 } 469 } 470 } finally { 471 synchronized (mLock) { 472 processVibratorCallbacks(); 473 } 474 } 475 } 476 477 /** 478 * Notify the vibrator completion. 479 * 480 * <p>This is a lightweight method that do not trigger any operation from {@link 481 * VibratorController}, so it can be called directly from a native callback. 482 */ 483 @GuardedBy("mLock") notifyVibratorComplete(int vibratorId)484 public void notifyVibratorComplete(int vibratorId) { 485 mNotifiedVibrators.offer(vibratorId); 486 if (!mWaitToProcessVibratorCallbacks) { 487 // No step is being played or cancelled now, process the callback right away. 488 processVibratorCallbacks(); 489 } 490 } 491 492 /** 493 * Cancel the current queue, replacing all remaining steps with respective clean-up steps. 494 * 495 * <p>This will remove all steps and replace them with respective 496 * {@link Step#cancel()}. 497 */ cancel()498 public void cancel() { 499 // Vibrator callbacks should wait until all steps from the queue are properly cancelled 500 // and clean up steps are added back to the queue, so they can handle the callback. 501 markWaitToProcessVibratorCallbacks(); 502 try { 503 List<Step> cleanUpSteps = new ArrayList<>(); 504 Step step; 505 while ((step = pollNext()) != null) { 506 cleanUpSteps.addAll(step.cancel()); 507 } 508 synchronized (mLock) { 509 // All steps generated by Step.cancel() should be clean-up steps. 510 mPendingVibrateSteps = 0; 511 mNextSteps.addAll(cleanUpSteps); 512 } 513 } finally { 514 synchronized (mLock) { 515 processVibratorCallbacks(); 516 } 517 } 518 } 519 520 /** 521 * Cancel the current queue immediately, clearing all remaining steps and skipping clean-up. 522 * 523 * <p>This will remove and trigger {@link Step#cancelImmediately()} in all steps, in order. 524 */ cancelImmediately()525 public void cancelImmediately() { 526 // Vibrator callbacks should wait until all steps from the queue are properly cancelled. 527 markWaitToProcessVibratorCallbacks(); 528 try { 529 Step step; 530 while ((step = pollNext()) != null) { 531 // This might turn off the vibrator and have a HAL latency. Execute this outside 532 // any lock to avoid blocking other interactions with the thread. 533 step.cancelImmediately(); 534 } 535 synchronized (mLock) { 536 mPendingVibrateSteps = 0; 537 } 538 } finally { 539 synchronized (mLock) { 540 processVibratorCallbacks(); 541 } 542 } 543 } 544 545 @Nullable pollNext()546 private Step pollNext() { 547 synchronized (mLock) { 548 // Prioritize the steps anticipated by a vibrator complete callback. 549 if (!mPendingOnVibratorCompleteSteps.isEmpty()) { 550 return mPendingOnVibratorCompleteSteps.poll(); 551 } 552 return mNextSteps.poll(); 553 } 554 } 555 markWaitToProcessVibratorCallbacks()556 private void markWaitToProcessVibratorCallbacks() { 557 synchronized (mLock) { 558 mWaitToProcessVibratorCallbacks = true; 559 } 560 } 561 562 /** 563 * Notify the step in this queue that should be anticipated by the vibrator completion 564 * callback and keep it separate to be consumed by {@link #consumeNext()}. 565 * 566 * <p>This is a lightweight method that do not trigger any operation from {@link 567 * VibratorController}, so it can be called directly from a native callback. 568 * 569 * <p>This assumes only one of the next steps is waiting on this given vibrator, so the 570 * first step found will be anticipated by this method, in no particular order. 571 */ 572 @GuardedBy("mLock") processVibratorCallbacks()573 private void processVibratorCallbacks() { 574 mWaitToProcessVibratorCallbacks = false; 575 while (!mNotifiedVibrators.isEmpty()) { 576 int vibratorId = mNotifiedVibrators.poll(); 577 Iterator<Step> it = mNextSteps.iterator(); 578 while (it.hasNext()) { 579 Step step = it.next(); 580 if (step.shouldPlayWhenVibratorComplete(vibratorId)) { 581 it.remove(); 582 mPendingOnVibratorCompleteSteps.offer(step); 583 break; 584 } 585 } 586 } 587 } 588 } 589 590 /** 591 * Represent a single step for playing a vibration. 592 * 593 * <p>Every step has a start time, which can be used to apply delays between steps while 594 * executing them in sequence. 595 */ 596 private abstract class Step implements Comparable<Step> { 597 public final long startTime; 598 Step(long startTime)599 Step(long startTime) { 600 this.startTime = startTime; 601 } 602 603 /** 604 * Returns true if this step is a clean up step and not part of a {@link VibrationEffect} or 605 * {@link CombinedVibration}. 606 */ isCleanUp()607 public boolean isCleanUp() { 608 return false; 609 } 610 611 /** Play this step, returning a (possibly empty) list of next steps. */ 612 @NonNull play()613 public abstract List<Step> play(); 614 615 /** 616 * Cancel this pending step and return a (possibly empty) list of clean-up steps that should 617 * be played to gracefully cancel this step. 618 */ 619 @NonNull cancel()620 public abstract List<Step> cancel(); 621 622 /** Cancel this pending step immediately, skipping any clean-up. */ cancelImmediately()623 public abstract void cancelImmediately(); 624 625 /** 626 * Return the duration the vibrator was turned on when this step was played. 627 * 628 * @return A positive duration that the vibrator was turned on for by this step; 629 * Zero if the segment is not supported, the step was not played yet or vibrator was never 630 * turned on by this step; A negative value if the vibrator call has failed. 631 */ getVibratorOnDuration()632 public long getVibratorOnDuration() { 633 return 0; 634 } 635 636 /** 637 * Return true to play this step right after a vibrator has notified vibration completed, 638 * used to anticipate steps waiting on vibrator callbacks with a timeout. 639 */ shouldPlayWhenVibratorComplete(int vibratorId)640 public boolean shouldPlayWhenVibratorComplete(int vibratorId) { 641 return false; 642 } 643 644 /** 645 * Returns the time in millis to wait before playing this step. This is performed 646 * while holding the queue lock, so should not rely on potentially slow operations. 647 */ calculateWaitTime()648 public long calculateWaitTime() { 649 if (startTime == Long.MAX_VALUE) { 650 // This step don't have a predefined start time, it's just marked to be executed 651 // after all other steps have finished. 652 return 0; 653 } 654 return Math.max(0, startTime - SystemClock.uptimeMillis()); 655 } 656 657 @Override compareTo(Step o)658 public int compareTo(Step o) { 659 return Long.compare(startTime, o.startTime); 660 } 661 } 662 663 /** 664 * Starts a sync vibration. 665 * 666 * <p>If this step has successfully started playing a vibration on any vibrator, it will always 667 * add a {@link FinishVibrateStep} to the queue, to be played after all vibrators have finished 668 * all their individual steps. 669 * 670 * <o>If this step does not start any vibrator, it will add a {@link StartVibrateStep} if the 671 * sequential effect isn't finished yet. 672 */ 673 private final class StartVibrateStep extends Step { 674 public final CombinedVibration.Sequential sequentialEffect; 675 public final int currentIndex; 676 677 private long mVibratorsOnMaxDuration; 678 StartVibrateStep(CombinedVibration.Sequential effect)679 StartVibrateStep(CombinedVibration.Sequential effect) { 680 this(SystemClock.uptimeMillis() + effect.getDelays().get(0), effect, /* index= */ 0); 681 } 682 StartVibrateStep(long startTime, CombinedVibration.Sequential effect, int index)683 StartVibrateStep(long startTime, CombinedVibration.Sequential effect, int index) { 684 super(startTime); 685 sequentialEffect = effect; 686 currentIndex = index; 687 } 688 689 @Override getVibratorOnDuration()690 public long getVibratorOnDuration() { 691 return mVibratorsOnMaxDuration; 692 } 693 694 @Override play()695 public List<Step> play() { 696 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "StartVibrateStep"); 697 List<Step> nextSteps = new ArrayList<>(); 698 mVibratorsOnMaxDuration = -1; 699 try { 700 if (DEBUG) { 701 Slog.d(TAG, "StartVibrateStep for effect #" + currentIndex); 702 } 703 CombinedVibration effect = sequentialEffect.getEffects().get(currentIndex); 704 DeviceEffectMap effectMapping = createEffectToVibratorMapping(effect); 705 if (effectMapping == null) { 706 // Unable to map effects to vibrators, ignore this step. 707 return nextSteps; 708 } 709 710 mVibratorsOnMaxDuration = startVibrating(effectMapping, nextSteps); 711 noteVibratorOn(mVibratorsOnMaxDuration); 712 } finally { 713 if (mVibratorsOnMaxDuration >= 0) { 714 // It least one vibrator was started then add a finish step to wait for all 715 // active vibrators to finish their individual steps before going to the next. 716 // Otherwise this step was ignored so just go to the next one. 717 Step nextStep = 718 mVibratorsOnMaxDuration > 0 ? new FinishVibrateStep(this) : nextStep(); 719 if (nextStep != null) { 720 nextSteps.add(nextStep); 721 } 722 } 723 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 724 } 725 return nextSteps; 726 } 727 728 @Override cancel()729 public List<Step> cancel() { 730 return EMPTY_STEP_LIST; 731 } 732 733 @Override cancelImmediately()734 public void cancelImmediately() { 735 } 736 737 /** 738 * Create the next {@link StartVibrateStep} to play this sequential effect, starting at the 739 * time this method is called, or null if sequence is complete. 740 */ 741 @Nullable nextStep()742 private Step nextStep() { 743 int nextIndex = currentIndex + 1; 744 if (nextIndex >= sequentialEffect.getEffects().size()) { 745 return null; 746 } 747 long nextEffectDelay = sequentialEffect.getDelays().get(nextIndex); 748 long nextStartTime = SystemClock.uptimeMillis() + nextEffectDelay; 749 return new StartVibrateStep(nextStartTime, sequentialEffect, nextIndex); 750 } 751 752 /** Create a mapping of individual {@link VibrationEffect} to available vibrators. */ 753 @Nullable createEffectToVibratorMapping( CombinedVibration effect)754 private DeviceEffectMap createEffectToVibratorMapping( 755 CombinedVibration effect) { 756 if (effect instanceof CombinedVibration.Mono) { 757 return new DeviceEffectMap((CombinedVibration.Mono) effect); 758 } 759 if (effect instanceof CombinedVibration.Stereo) { 760 return new DeviceEffectMap((CombinedVibration.Stereo) effect); 761 } 762 return null; 763 } 764 765 /** 766 * Starts playing effects on designated vibrators, in sync. 767 * 768 * @param effectMapping The {@link CombinedVibration} mapped to this device vibrators 769 * @param nextSteps An output list to accumulate the future {@link Step Steps} created 770 * by this method, typically one for each vibrator that has 771 * successfully started vibrating on this step. 772 * @return The duration, in millis, of the {@link CombinedVibration}. Repeating 773 * waveforms return {@link Long#MAX_VALUE}. Zero or negative values indicate the vibrators 774 * have ignored all effects. 775 */ startVibrating(DeviceEffectMap effectMapping, List<Step> nextSteps)776 private long startVibrating(DeviceEffectMap effectMapping, List<Step> nextSteps) { 777 int vibratorCount = effectMapping.size(); 778 if (vibratorCount == 0) { 779 // No effect was mapped to any available vibrator. 780 return 0; 781 } 782 783 SingleVibratorStep[] steps = new SingleVibratorStep[vibratorCount]; 784 long vibrationStartTime = SystemClock.uptimeMillis(); 785 for (int i = 0; i < vibratorCount; i++) { 786 steps[i] = nextVibrateStep(vibrationStartTime, 787 mVibrators.get(effectMapping.vibratorIdAt(i)), 788 effectMapping.effectAt(i), 789 /* segmentIndex= */ 0, /* vibratorOffTimeout= */ 0); 790 } 791 792 if (steps.length == 1) { 793 // No need to prepare and trigger sync effects on a single vibrator. 794 return startVibrating(steps[0], nextSteps); 795 } 796 797 // This synchronization of vibrators should be executed one at a time, even if we are 798 // vibrating different sets of vibrators in parallel. The manager can only prepareSynced 799 // one set of vibrators at a time. 800 synchronized (mLock) { 801 boolean hasPrepared = false; 802 boolean hasTriggered = false; 803 long maxDuration = 0; 804 try { 805 hasPrepared = mCallbacks.prepareSyncedVibration( 806 effectMapping.getRequiredSyncCapabilities(), 807 effectMapping.getVibratorIds()); 808 809 for (SingleVibratorStep step : steps) { 810 long duration = startVibrating(step, nextSteps); 811 if (duration < 0) { 812 // One vibrator has failed, fail this entire sync attempt. 813 return maxDuration = -1; 814 } 815 maxDuration = Math.max(maxDuration, duration); 816 } 817 818 // Check if sync was prepared and if any step was accepted by a vibrator, 819 // otherwise there is nothing to trigger here. 820 if (hasPrepared && maxDuration > 0) { 821 hasTriggered = mCallbacks.triggerSyncedVibration(mVibration.id); 822 } 823 return maxDuration; 824 } finally { 825 if (hasPrepared && !hasTriggered) { 826 // Trigger has failed or all steps were ignored by the vibrators. 827 mCallbacks.cancelSyncedVibration(); 828 nextSteps.clear(); 829 } else if (maxDuration < 0) { 830 // Some vibrator failed without being prepared so other vibrators might be 831 // active. Cancel and remove every pending step from output list. 832 for (int i = nextSteps.size() - 1; i >= 0; i--) { 833 nextSteps.remove(i).cancelImmediately(); 834 } 835 } 836 } 837 } 838 } 839 startVibrating(SingleVibratorStep step, List<Step> nextSteps)840 private long startVibrating(SingleVibratorStep step, List<Step> nextSteps) { 841 nextSteps.addAll(step.play()); 842 long stepDuration = step.getVibratorOnDuration(); 843 if (stepDuration < 0) { 844 // Step failed, so return negative duration to propagate failure. 845 return stepDuration; 846 } 847 // Return the longest estimation for the entire effect. 848 return Math.max(stepDuration, step.effect.getDuration()); 849 } 850 } 851 852 /** 853 * Finish a sync vibration started by a {@link StartVibrateStep}. 854 * 855 * <p>This only plays after all active vibrators steps have finished, and adds a {@link 856 * StartVibrateStep} to the queue if the sequential effect isn't finished yet. 857 */ 858 private final class FinishVibrateStep extends Step { 859 public final StartVibrateStep startedStep; 860 FinishVibrateStep(StartVibrateStep startedStep)861 FinishVibrateStep(StartVibrateStep startedStep) { 862 super(Long.MAX_VALUE); // No predefined startTime, just wait for all steps in the queue. 863 this.startedStep = startedStep; 864 } 865 866 @Override isCleanUp()867 public boolean isCleanUp() { 868 // This step only notes that all the vibrators has been turned off. 869 return true; 870 } 871 872 @Override play()873 public List<Step> play() { 874 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "FinishVibrateStep"); 875 try { 876 if (DEBUG) { 877 Slog.d(TAG, "FinishVibrateStep for effect #" + startedStep.currentIndex); 878 } 879 noteVibratorOff(); 880 Step nextStep = startedStep.nextStep(); 881 return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep); 882 } finally { 883 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 884 } 885 } 886 887 @Override cancel()888 public List<Step> cancel() { 889 cancelImmediately(); 890 return EMPTY_STEP_LIST; 891 } 892 893 @Override cancelImmediately()894 public void cancelImmediately() { 895 noteVibratorOff(); 896 } 897 } 898 899 /** 900 * Represent a step on a single vibrator that plays one or more segments from a 901 * {@link VibrationEffect.Composed} effect. 902 */ 903 private abstract class SingleVibratorStep extends Step { 904 public final VibratorController controller; 905 public final VibrationEffect.Composed effect; 906 public final int segmentIndex; 907 public final long vibratorOffTimeout; 908 909 long mVibratorOnResult; 910 boolean mVibratorCallbackReceived; 911 912 /** 913 * @param startTime The time to schedule this step in the {@link StepQueue}. 914 * @param controller The vibrator that is playing the effect. 915 * @param effect The effect being played in this step. 916 * @param index The index of the next segment to be played by this step 917 * @param vibratorOffTimeout The time the vibrator is expected to complete any previous 918 * vibration and turn off. This is used to allow this step to be 919 * anticipated when the completion callback is triggered, and can 920 * be used play effects back-to-back. 921 */ SingleVibratorStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)922 SingleVibratorStep(long startTime, VibratorController controller, 923 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) { 924 super(startTime); 925 this.controller = controller; 926 this.effect = effect; 927 this.segmentIndex = index; 928 this.vibratorOffTimeout = vibratorOffTimeout; 929 } 930 931 @Override getVibratorOnDuration()932 public long getVibratorOnDuration() { 933 return mVibratorOnResult; 934 } 935 936 @Override shouldPlayWhenVibratorComplete(int vibratorId)937 public boolean shouldPlayWhenVibratorComplete(int vibratorId) { 938 boolean isSameVibrator = controller.getVibratorInfo().getId() == vibratorId; 939 mVibratorCallbackReceived |= isSameVibrator; 940 // Only anticipate this step if a timeout was set to wait for the vibration to complete, 941 // otherwise we are waiting for the correct time to play the next step. 942 return isSameVibrator && (vibratorOffTimeout > SystemClock.uptimeMillis()); 943 } 944 945 @Override cancel()946 public List<Step> cancel() { 947 return Arrays.asList(new CompleteStep(SystemClock.uptimeMillis(), 948 /* cancelled= */ true, controller, vibratorOffTimeout)); 949 } 950 951 @Override cancelImmediately()952 public void cancelImmediately() { 953 if (vibratorOffTimeout > SystemClock.uptimeMillis()) { 954 // Vibrator might be running from previous steps, so turn it off while canceling. 955 stopVibrating(); 956 } 957 } 958 stopVibrating()959 void stopVibrating() { 960 if (DEBUG) { 961 Slog.d(TAG, "Turning off vibrator " + controller.getVibratorInfo().getId()); 962 } 963 controller.off(); 964 } 965 changeAmplitude(float amplitude)966 void changeAmplitude(float amplitude) { 967 if (DEBUG) { 968 Slog.d(TAG, "Amplitude changed on vibrator " + controller.getVibratorInfo().getId() 969 + " to " + amplitude); 970 } 971 controller.setAmplitude(amplitude); 972 } 973 974 /** Return the {@link #nextVibrateStep} with same timings, only jumping the segments. */ skipToNextSteps(int segmentsSkipped)975 public List<Step> skipToNextSteps(int segmentsSkipped) { 976 return nextSteps(startTime, vibratorOffTimeout, segmentsSkipped); 977 } 978 979 /** 980 * Return the {@link #nextVibrateStep} with same start and off timings calculated from 981 * {@link #getVibratorOnDuration()}, jumping all played segments. 982 * 983 * <p>This method has same behavior as {@link #skipToNextSteps(int)} when the vibrator 984 * result is non-positive, meaning the vibrator has either ignored or failed to turn on. 985 */ nextSteps(int segmentsPlayed)986 public List<Step> nextSteps(int segmentsPlayed) { 987 if (mVibratorOnResult <= 0) { 988 // Vibration was not started, so just skip the played segments and keep timings. 989 return skipToNextSteps(segmentsPlayed); 990 } 991 long nextStartTime = SystemClock.uptimeMillis() + mVibratorOnResult; 992 long nextVibratorOffTimeout = nextStartTime + CALLBACKS_EXTRA_TIMEOUT; 993 return nextSteps(nextStartTime, nextVibratorOffTimeout, segmentsPlayed); 994 } 995 996 /** 997 * Return the {@link #nextVibrateStep} with given start and off timings, which might be 998 * calculated independently, jumping all played segments. 999 * 1000 * <p>This should be used when the vibrator on/off state is not responsible for the steps 1001 * execution timings, e.g. while playing the vibrator amplitudes. 1002 */ nextSteps(long nextStartTime, long vibratorOffTimeout, int segmentsPlayed)1003 public List<Step> nextSteps(long nextStartTime, long vibratorOffTimeout, 1004 int segmentsPlayed) { 1005 Step nextStep = nextVibrateStep(nextStartTime, controller, effect, 1006 segmentIndex + segmentsPlayed, vibratorOffTimeout); 1007 return nextStep == null ? EMPTY_STEP_LIST : Arrays.asList(nextStep); 1008 } 1009 } 1010 1011 /** 1012 * Represent a step turn the vibrator on with a single prebaked effect. 1013 * 1014 * <p>This step automatically falls back by replacing the prebaked segment with 1015 * {@link VibrationSettings#getFallbackEffect(int)}, if available. 1016 */ 1017 private final class PerformStep extends SingleVibratorStep { 1018 PerformStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)1019 PerformStep(long startTime, VibratorController controller, 1020 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) { 1021 // This step should wait for the last vibration to finish (with the timeout) and for the 1022 // intended step start time (to respect the effect delays). 1023 super(Math.max(startTime, vibratorOffTimeout), controller, effect, index, 1024 vibratorOffTimeout); 1025 } 1026 1027 @Override play()1028 public List<Step> play() { 1029 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "PerformStep"); 1030 try { 1031 VibrationEffectSegment segment = effect.getSegments().get(segmentIndex); 1032 if (!(segment instanceof PrebakedSegment)) { 1033 Slog.w(TAG, "Ignoring wrong segment for a PerformStep: " + segment); 1034 return skipToNextSteps(/* segmentsSkipped= */ 1); 1035 } 1036 1037 PrebakedSegment prebaked = (PrebakedSegment) segment; 1038 if (DEBUG) { 1039 Slog.d(TAG, "Perform " + VibrationEffect.effectIdToString( 1040 prebaked.getEffectId()) + " on vibrator " 1041 + controller.getVibratorInfo().getId()); 1042 } 1043 1044 VibrationEffect fallback = mVibration.getFallback(prebaked.getEffectId()); 1045 mVibratorOnResult = controller.on(prebaked, mVibration.id); 1046 1047 if (mVibratorOnResult == 0 && prebaked.shouldFallback() 1048 && (fallback instanceof VibrationEffect.Composed)) { 1049 if (DEBUG) { 1050 Slog.d(TAG, "Playing fallback for effect " 1051 + VibrationEffect.effectIdToString(prebaked.getEffectId())); 1052 } 1053 SingleVibratorStep fallbackStep = nextVibrateStep(startTime, controller, 1054 replaceCurrentSegment((VibrationEffect.Composed) fallback), 1055 segmentIndex, vibratorOffTimeout); 1056 List<Step> fallbackResult = fallbackStep.play(); 1057 // Update the result with the fallback result so this step is seamlessly 1058 // replaced by the fallback to any outer application of this. 1059 mVibratorOnResult = fallbackStep.getVibratorOnDuration(); 1060 return fallbackResult; 1061 } 1062 1063 return nextSteps(/* segmentsPlayed= */ 1); 1064 } finally { 1065 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1066 } 1067 } 1068 1069 /** 1070 * Replace segment at {@link #segmentIndex} in {@link #effect} with given fallback segments. 1071 * 1072 * @return a copy of {@link #effect} with replaced segment. 1073 */ replaceCurrentSegment(VibrationEffect.Composed fallback)1074 private VibrationEffect.Composed replaceCurrentSegment(VibrationEffect.Composed fallback) { 1075 List<VibrationEffectSegment> newSegments = new ArrayList<>(effect.getSegments()); 1076 int newRepeatIndex = effect.getRepeatIndex(); 1077 newSegments.remove(segmentIndex); 1078 newSegments.addAll(segmentIndex, fallback.getSegments()); 1079 if (segmentIndex < effect.getRepeatIndex()) { 1080 newRepeatIndex += fallback.getSegments().size(); 1081 } 1082 return new VibrationEffect.Composed(newSegments, newRepeatIndex); 1083 } 1084 } 1085 1086 /** 1087 * Represent a step turn the vibrator on using a composition of primitives. 1088 * 1089 * <p>This step will use the maximum supported number of consecutive segments of type 1090 * {@link PrimitiveSegment} starting at the current index. 1091 */ 1092 private final class ComposePrimitivesStep extends SingleVibratorStep { 1093 ComposePrimitivesStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)1094 ComposePrimitivesStep(long startTime, VibratorController controller, 1095 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) { 1096 // This step should wait for the last vibration to finish (with the timeout) and for the 1097 // intended step start time (to respect the effect delays). 1098 super(Math.max(startTime, vibratorOffTimeout), controller, effect, index, 1099 vibratorOffTimeout); 1100 } 1101 1102 @Override play()1103 public List<Step> play() { 1104 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePrimitivesStep"); 1105 try { 1106 // Load the next PrimitiveSegments to create a single compose call to the vibrator, 1107 // limited to the vibrator composition maximum size. 1108 int limit = controller.getVibratorInfo().getCompositionSizeMax(); 1109 int segmentCount = limit > 0 1110 ? Math.min(effect.getSegments().size(), segmentIndex + limit) 1111 : effect.getSegments().size(); 1112 List<PrimitiveSegment> primitives = new ArrayList<>(); 1113 for (int i = segmentIndex; i < segmentCount; i++) { 1114 VibrationEffectSegment segment = effect.getSegments().get(i); 1115 if (segment instanceof PrimitiveSegment) { 1116 primitives.add((PrimitiveSegment) segment); 1117 } else { 1118 break; 1119 } 1120 } 1121 1122 if (primitives.isEmpty()) { 1123 Slog.w(TAG, "Ignoring wrong segment for a ComposePrimitivesStep: " 1124 + effect.getSegments().get(segmentIndex)); 1125 return skipToNextSteps(/* segmentsSkipped= */ 1); 1126 } 1127 1128 if (DEBUG) { 1129 Slog.d(TAG, "Compose " + primitives + " primitives on vibrator " 1130 + controller.getVibratorInfo().getId()); 1131 } 1132 mVibratorOnResult = controller.on( 1133 primitives.toArray(new PrimitiveSegment[primitives.size()]), 1134 mVibration.id); 1135 1136 return nextSteps(/* segmentsPlayed= */ primitives.size()); 1137 } finally { 1138 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1139 } 1140 } 1141 } 1142 1143 /** 1144 * Represent a step turn the vibrator on using a composition of PWLE segments. 1145 * 1146 * <p>This step will use the maximum supported number of consecutive segments of type 1147 * {@link StepSegment} or {@link RampSegment} starting at the current index. 1148 */ 1149 private final class ComposePwleStep extends SingleVibratorStep { 1150 ComposePwleStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)1151 ComposePwleStep(long startTime, VibratorController controller, 1152 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) { 1153 // This step should wait for the last vibration to finish (with the timeout) and for the 1154 // intended step start time (to respect the effect delays). 1155 super(Math.max(startTime, vibratorOffTimeout), controller, effect, index, 1156 vibratorOffTimeout); 1157 } 1158 1159 @Override play()1160 public List<Step> play() { 1161 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "ComposePwleStep"); 1162 try { 1163 // Load the next RampSegments to create a single composePwle call to the vibrator, 1164 // limited to the vibrator PWLE maximum size. 1165 int limit = controller.getVibratorInfo().getPwleSizeMax(); 1166 int segmentCount = limit > 0 1167 ? Math.min(effect.getSegments().size(), segmentIndex + limit) 1168 : effect.getSegments().size(); 1169 List<RampSegment> pwles = new ArrayList<>(); 1170 for (int i = segmentIndex; i < segmentCount; i++) { 1171 VibrationEffectSegment segment = effect.getSegments().get(i); 1172 if (segment instanceof RampSegment) { 1173 pwles.add((RampSegment) segment); 1174 } else { 1175 break; 1176 } 1177 } 1178 1179 if (pwles.isEmpty()) { 1180 Slog.w(TAG, "Ignoring wrong segment for a ComposePwleStep: " 1181 + effect.getSegments().get(segmentIndex)); 1182 return skipToNextSteps(/* segmentsSkipped= */ 1); 1183 } 1184 1185 if (DEBUG) { 1186 Slog.d(TAG, "Compose " + pwles + " PWLEs on vibrator " 1187 + controller.getVibratorInfo().getId()); 1188 } 1189 mVibratorOnResult = controller.on(pwles.toArray(new RampSegment[pwles.size()]), 1190 mVibration.id); 1191 1192 return nextSteps(/* segmentsPlayed= */ pwles.size()); 1193 } finally { 1194 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1195 } 1196 } 1197 } 1198 1199 /** 1200 * Represents a step to complete a {@link VibrationEffect}. 1201 * 1202 * <p>This runs right at the time the vibration is considered to end and will update the pending 1203 * vibrators count. This can turn off the vibrator or slowly ramp it down to zero amplitude. 1204 */ 1205 private final class CompleteStep extends SingleVibratorStep { 1206 private final boolean mCancelled; 1207 CompleteStep(long startTime, boolean cancelled, VibratorController controller, long vibratorOffTimeout)1208 CompleteStep(long startTime, boolean cancelled, VibratorController controller, 1209 long vibratorOffTimeout) { 1210 super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout); 1211 mCancelled = cancelled; 1212 } 1213 1214 @Override isCleanUp()1215 public boolean isCleanUp() { 1216 // If the vibration was cancelled then this is just a clean up to ramp off the vibrator. 1217 // Otherwise this step is part of the vibration. 1218 return mCancelled; 1219 } 1220 1221 @Override cancel()1222 public List<Step> cancel() { 1223 if (mCancelled) { 1224 // Double cancelling will just turn off the vibrator right away. 1225 return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller)); 1226 } 1227 return super.cancel(); 1228 } 1229 1230 @Override play()1231 public List<Step> play() { 1232 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "CompleteStep"); 1233 try { 1234 if (DEBUG) { 1235 Slog.d(TAG, "Running " + (mCancelled ? "cancel" : "complete") + " vibration" 1236 + " step on vibrator " + controller.getVibratorInfo().getId()); 1237 } 1238 if (mVibratorCallbackReceived) { 1239 // Vibration completion callback was received by this step, just turn if off 1240 // and skip any clean-up. 1241 stopVibrating(); 1242 return EMPTY_STEP_LIST; 1243 } 1244 1245 float currentAmplitude = controller.getCurrentAmplitude(); 1246 long remainingOnDuration = 1247 vibratorOffTimeout - CALLBACKS_EXTRA_TIMEOUT - SystemClock.uptimeMillis(); 1248 long rampDownDuration = 1249 Math.min(remainingOnDuration, mVibrationSettings.getRampDownDuration()); 1250 long stepDownDuration = mVibrationSettings.getRampStepDuration(); 1251 if (currentAmplitude < RAMP_OFF_AMPLITUDE_MIN 1252 || rampDownDuration <= stepDownDuration) { 1253 // No need to ramp down the amplitude, just wait to turn it off. 1254 if (mCancelled) { 1255 // Vibration is completing because it was cancelled, turn off right away. 1256 stopVibrating(); 1257 return EMPTY_STEP_LIST; 1258 } else { 1259 return Arrays.asList(new OffStep(vibratorOffTimeout, controller)); 1260 } 1261 } 1262 1263 if (DEBUG) { 1264 Slog.d(TAG, "Ramping down vibrator " + controller.getVibratorInfo().getId() 1265 + " from amplitude " + currentAmplitude 1266 + " for " + rampDownDuration + "ms"); 1267 } 1268 float amplitudeDelta = currentAmplitude / (rampDownDuration / stepDownDuration); 1269 float amplitudeTarget = currentAmplitude - amplitudeDelta; 1270 long newVibratorOffTimeout = mCancelled ? rampDownDuration : vibratorOffTimeout; 1271 return Arrays.asList(new RampOffStep(startTime, amplitudeTarget, amplitudeDelta, 1272 controller, newVibratorOffTimeout)); 1273 } finally { 1274 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1275 } 1276 } 1277 } 1278 1279 /** Represents a step to ramp down the vibrator amplitude before turning it off. */ 1280 private final class RampOffStep extends SingleVibratorStep { 1281 private final float mAmplitudeTarget; 1282 private final float mAmplitudeDelta; 1283 RampOffStep(long startTime, float amplitudeTarget, float amplitudeDelta, VibratorController controller, long vibratorOffTimeout)1284 RampOffStep(long startTime, float amplitudeTarget, float amplitudeDelta, 1285 VibratorController controller, long vibratorOffTimeout) { 1286 super(startTime, controller, /* effect= */ null, /* index= */ -1, vibratorOffTimeout); 1287 mAmplitudeTarget = amplitudeTarget; 1288 mAmplitudeDelta = amplitudeDelta; 1289 } 1290 1291 @Override isCleanUp()1292 public boolean isCleanUp() { 1293 return true; 1294 } 1295 1296 @Override cancel()1297 public List<Step> cancel() { 1298 return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller)); 1299 } 1300 1301 @Override play()1302 public List<Step> play() { 1303 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "RampOffStep"); 1304 try { 1305 if (DEBUG) { 1306 long latency = SystemClock.uptimeMillis() - startTime; 1307 Slog.d(TAG, "Ramp down the vibrator amplitude, step with " 1308 + latency + "ms latency."); 1309 } 1310 if (mVibratorCallbackReceived) { 1311 // Vibration completion callback was received by this step, just turn if off 1312 // and skip the rest of the steps to ramp down the vibrator amplitude. 1313 stopVibrating(); 1314 return EMPTY_STEP_LIST; 1315 } 1316 1317 changeAmplitude(mAmplitudeTarget); 1318 1319 float newAmplitudeTarget = mAmplitudeTarget - mAmplitudeDelta; 1320 if (newAmplitudeTarget < RAMP_OFF_AMPLITUDE_MIN) { 1321 // Vibrator amplitude cannot go further down, just turn it off. 1322 return Arrays.asList(new OffStep(vibratorOffTimeout, controller)); 1323 } 1324 return Arrays.asList(new RampOffStep( 1325 startTime + mVibrationSettings.getRampStepDuration(), newAmplitudeTarget, 1326 mAmplitudeDelta, controller, vibratorOffTimeout)); 1327 } finally { 1328 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1329 } 1330 } 1331 } 1332 1333 /** 1334 * Represents a step to turn the vibrator off. 1335 * 1336 * <p>This runs after a timeout on the expected time the vibrator should have finished playing, 1337 * and can anticipated by vibrator complete callbacks. 1338 */ 1339 private final class OffStep extends SingleVibratorStep { 1340 OffStep(long startTime, VibratorController controller)1341 OffStep(long startTime, VibratorController controller) { 1342 super(startTime, controller, /* effect= */ null, /* index= */ -1, startTime); 1343 } 1344 1345 @Override isCleanUp()1346 public boolean isCleanUp() { 1347 return true; 1348 } 1349 1350 @Override cancel()1351 public List<Step> cancel() { 1352 return Arrays.asList(new OffStep(SystemClock.uptimeMillis(), controller)); 1353 } 1354 1355 @Override cancelImmediately()1356 public void cancelImmediately() { 1357 stopVibrating(); 1358 } 1359 1360 @Override play()1361 public List<Step> play() { 1362 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "OffStep"); 1363 try { 1364 stopVibrating(); 1365 return EMPTY_STEP_LIST; 1366 } finally { 1367 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1368 } 1369 } 1370 } 1371 1372 /** 1373 * Represents a step to turn the vibrator on and change its amplitude. 1374 * 1375 * <p>This step ignores vibration completion callbacks and control the vibrator on/off state 1376 * and amplitude to simulate waveforms represented by a sequence of {@link StepSegment}. 1377 */ 1378 private final class AmplitudeStep extends SingleVibratorStep { 1379 private long mNextOffTime; 1380 AmplitudeStep(long startTime, VibratorController controller, VibrationEffect.Composed effect, int index, long vibratorOffTimeout)1381 AmplitudeStep(long startTime, VibratorController controller, 1382 VibrationEffect.Composed effect, int index, long vibratorOffTimeout) { 1383 // This step has a fixed startTime coming from the timings of the waveform it's playing. 1384 super(startTime, controller, effect, index, vibratorOffTimeout); 1385 mNextOffTime = vibratorOffTimeout; 1386 } 1387 1388 @Override shouldPlayWhenVibratorComplete(int vibratorId)1389 public boolean shouldPlayWhenVibratorComplete(int vibratorId) { 1390 if (controller.getVibratorInfo().getId() == vibratorId) { 1391 mVibratorCallbackReceived = true; 1392 mNextOffTime = SystemClock.uptimeMillis(); 1393 } 1394 // Timings are tightly controlled here, so only anticipate if the vibrator was supposed 1395 // to be ON but has completed prematurely, to turn it back on as soon as possible. 1396 return mNextOffTime < startTime && controller.getCurrentAmplitude() > 0; 1397 } 1398 1399 @Override play()1400 public List<Step> play() { 1401 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "AmplitudeStep"); 1402 try { 1403 long now = SystemClock.uptimeMillis(); 1404 long latency = now - startTime; 1405 if (DEBUG) { 1406 Slog.d(TAG, "Running amplitude step with " + latency + "ms latency."); 1407 } 1408 1409 if (mVibratorCallbackReceived && latency < 0) { 1410 // This step was anticipated because the vibrator turned off prematurely. 1411 // Turn it back on and return this same step to run at the exact right time. 1412 mNextOffTime = turnVibratorBackOn(/* remainingDuration= */ -latency); 1413 return Arrays.asList(new AmplitudeStep(startTime, controller, effect, 1414 segmentIndex, mNextOffTime)); 1415 } 1416 1417 VibrationEffectSegment segment = effect.getSegments().get(segmentIndex); 1418 if (!(segment instanceof StepSegment)) { 1419 Slog.w(TAG, "Ignoring wrong segment for a AmplitudeStep: " + segment); 1420 return skipToNextSteps(/* segmentsSkipped= */ 1); 1421 } 1422 1423 StepSegment stepSegment = (StepSegment) segment; 1424 if (stepSegment.getDuration() == 0) { 1425 // Skip waveform entries with zero timing. 1426 return skipToNextSteps(/* segmentsSkipped= */ 1); 1427 } 1428 1429 float amplitude = stepSegment.getAmplitude(); 1430 if (amplitude == 0) { 1431 if (vibratorOffTimeout > now) { 1432 // Amplitude cannot be set to zero, so stop the vibrator. 1433 stopVibrating(); 1434 mNextOffTime = now; 1435 } 1436 } else { 1437 if (startTime >= mNextOffTime) { 1438 // Vibrator is OFF. Turn vibrator back on for the duration of another 1439 // cycle before setting the amplitude. 1440 long onDuration = getVibratorOnDuration(effect, segmentIndex); 1441 if (onDuration > 0) { 1442 mVibratorOnResult = startVibrating(onDuration); 1443 mNextOffTime = now + onDuration + CALLBACKS_EXTRA_TIMEOUT; 1444 } 1445 } 1446 changeAmplitude(amplitude); 1447 } 1448 1449 // Use original startTime to avoid propagating latencies to the waveform. 1450 long nextStartTime = startTime + segment.getDuration(); 1451 return nextSteps(nextStartTime, mNextOffTime, /* segmentsPlayed= */ 1); 1452 } finally { 1453 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1454 } 1455 } 1456 turnVibratorBackOn(long remainingDuration)1457 private long turnVibratorBackOn(long remainingDuration) { 1458 long onDuration = getVibratorOnDuration(effect, segmentIndex); 1459 if (onDuration <= 0) { 1460 // Vibrator is supposed to go back off when this step starts, so just leave it off. 1461 return vibratorOffTimeout; 1462 } 1463 onDuration += remainingDuration; 1464 float expectedAmplitude = controller.getCurrentAmplitude(); 1465 mVibratorOnResult = startVibrating(onDuration); 1466 if (mVibratorOnResult > 0) { 1467 // Set the amplitude back to the value it was supposed to be playing at. 1468 changeAmplitude(expectedAmplitude); 1469 } 1470 return SystemClock.uptimeMillis() + onDuration + CALLBACKS_EXTRA_TIMEOUT; 1471 } 1472 startVibrating(long duration)1473 private long startVibrating(long duration) { 1474 if (DEBUG) { 1475 Slog.d(TAG, "Turning on vibrator " + controller.getVibratorInfo().getId() + " for " 1476 + duration + "ms"); 1477 } 1478 return controller.on(duration, mVibration.id); 1479 } 1480 1481 /** 1482 * Get the duration the vibrator will be on for a waveform, starting at {@code startIndex} 1483 * until the next time it's vibrating amplitude is zero or a different type of segment is 1484 * found. 1485 */ getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex)1486 private long getVibratorOnDuration(VibrationEffect.Composed effect, int startIndex) { 1487 List<VibrationEffectSegment> segments = effect.getSegments(); 1488 int segmentCount = segments.size(); 1489 int repeatIndex = effect.getRepeatIndex(); 1490 int i = startIndex; 1491 long timing = 0; 1492 while (i < segmentCount) { 1493 VibrationEffectSegment segment = segments.get(i); 1494 if (!(segment instanceof StepSegment) 1495 || ((StepSegment) segment).getAmplitude() == 0) { 1496 break; 1497 } 1498 timing += segment.getDuration(); 1499 i++; 1500 if (i == segmentCount && repeatIndex >= 0) { 1501 i = repeatIndex; 1502 // prevent infinite loop 1503 repeatIndex = -1; 1504 } 1505 if (i == startIndex) { 1506 // The repeating waveform keeps the vibrator ON all the time. Use a minimum 1507 // of 1s duration to prevent short patterns from turning the vibrator ON too 1508 // frequently. 1509 return Math.max(timing, 1000); 1510 } 1511 } 1512 if (i == segmentCount && effect.getRepeatIndex() < 0) { 1513 // Vibration ending at non-zero amplitude, add extra timings to ramp down after 1514 // vibration is complete. 1515 timing += mVibrationSettings.getRampDownDuration(); 1516 } 1517 return timing; 1518 } 1519 } 1520 1521 /** 1522 * Map a {@link CombinedVibration} to the vibrators available on the device. 1523 * 1524 * <p>This contains the logic to find the capabilities required from {@link IVibratorManager} to 1525 * play all of the effects in sync. 1526 */ 1527 private final class DeviceEffectMap { 1528 private final SparseArray<VibrationEffect.Composed> mVibratorEffects; 1529 private final int[] mVibratorIds; 1530 private final long mRequiredSyncCapabilities; 1531 DeviceEffectMap(CombinedVibration.Mono mono)1532 DeviceEffectMap(CombinedVibration.Mono mono) { 1533 mVibratorEffects = new SparseArray<>(mVibrators.size()); 1534 mVibratorIds = new int[mVibrators.size()]; 1535 for (int i = 0; i < mVibrators.size(); i++) { 1536 int vibratorId = mVibrators.keyAt(i); 1537 VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo(); 1538 VibrationEffect effect = mDeviceEffectAdapter.apply(mono.getEffect(), vibratorInfo); 1539 if (effect instanceof VibrationEffect.Composed) { 1540 mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect); 1541 mVibratorIds[i] = vibratorId; 1542 } 1543 } 1544 mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects); 1545 } 1546 DeviceEffectMap(CombinedVibration.Stereo stereo)1547 DeviceEffectMap(CombinedVibration.Stereo stereo) { 1548 SparseArray<VibrationEffect> stereoEffects = stereo.getEffects(); 1549 mVibratorEffects = new SparseArray<>(); 1550 for (int i = 0; i < stereoEffects.size(); i++) { 1551 int vibratorId = stereoEffects.keyAt(i); 1552 if (mVibrators.contains(vibratorId)) { 1553 VibratorInfo vibratorInfo = mVibrators.valueAt(i).getVibratorInfo(); 1554 VibrationEffect effect = mDeviceEffectAdapter.apply( 1555 stereoEffects.valueAt(i), vibratorInfo); 1556 if (effect instanceof VibrationEffect.Composed) { 1557 mVibratorEffects.put(vibratorId, (VibrationEffect.Composed) effect); 1558 } 1559 } 1560 } 1561 mVibratorIds = new int[mVibratorEffects.size()]; 1562 for (int i = 0; i < mVibratorEffects.size(); i++) { 1563 mVibratorIds[i] = mVibratorEffects.keyAt(i); 1564 } 1565 mRequiredSyncCapabilities = calculateRequiredSyncCapabilities(mVibratorEffects); 1566 } 1567 1568 /** 1569 * Return the number of vibrators mapped to play the {@link CombinedVibration} on this 1570 * device. 1571 */ size()1572 public int size() { 1573 return mVibratorIds.length; 1574 } 1575 1576 /** 1577 * Return all capabilities required to play the {@link CombinedVibration} in 1578 * between calls to {@link IVibratorManager#prepareSynced} and 1579 * {@link IVibratorManager#triggerSynced}. 1580 */ getRequiredSyncCapabilities()1581 public long getRequiredSyncCapabilities() { 1582 return mRequiredSyncCapabilities; 1583 } 1584 1585 /** Return all vibrator ids mapped to play the {@link CombinedVibration}. */ getVibratorIds()1586 public int[] getVibratorIds() { 1587 return mVibratorIds; 1588 } 1589 1590 /** Return the id of the vibrator at given index. */ vibratorIdAt(int index)1591 public int vibratorIdAt(int index) { 1592 return mVibratorEffects.keyAt(index); 1593 } 1594 1595 /** Return the {@link VibrationEffect} at given index. */ effectAt(int index)1596 public VibrationEffect.Composed effectAt(int index) { 1597 return mVibratorEffects.valueAt(index); 1598 } 1599 1600 /** 1601 * Return all capabilities required from the {@link IVibratorManager} to prepare and 1602 * trigger all given effects in sync. 1603 * 1604 * @return {@link IVibratorManager#CAP_SYNC} together with all required 1605 * IVibratorManager.CAP_PREPARE_* and IVibratorManager.CAP_MIXED_TRIGGER_* capabilities. 1606 */ calculateRequiredSyncCapabilities( SparseArray<VibrationEffect.Composed> effects)1607 private long calculateRequiredSyncCapabilities( 1608 SparseArray<VibrationEffect.Composed> effects) { 1609 long prepareCap = 0; 1610 for (int i = 0; i < effects.size(); i++) { 1611 VibrationEffectSegment firstSegment = effects.valueAt(i).getSegments().get(0); 1612 if (firstSegment instanceof StepSegment) { 1613 prepareCap |= IVibratorManager.CAP_PREPARE_ON; 1614 } else if (firstSegment instanceof PrebakedSegment) { 1615 prepareCap |= IVibratorManager.CAP_PREPARE_PERFORM; 1616 } else if (firstSegment instanceof PrimitiveSegment) { 1617 prepareCap |= IVibratorManager.CAP_PREPARE_COMPOSE; 1618 } 1619 } 1620 int triggerCap = 0; 1621 if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_ON)) { 1622 triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_ON; 1623 } 1624 if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_PERFORM)) { 1625 triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_PERFORM; 1626 } 1627 if (requireMixedTriggerCapability(prepareCap, IVibratorManager.CAP_PREPARE_COMPOSE)) { 1628 triggerCap |= IVibratorManager.CAP_MIXED_TRIGGER_COMPOSE; 1629 } 1630 return IVibratorManager.CAP_SYNC | prepareCap | triggerCap; 1631 } 1632 1633 /** 1634 * Return true if {@code prepareCapabilities} contains this {@code capability} mixed with 1635 * different ones, requiring a mixed trigger capability from the vibrator manager for 1636 * syncing all effects. 1637 */ requireMixedTriggerCapability(long prepareCapabilities, long capability)1638 private boolean requireMixedTriggerCapability(long prepareCapabilities, long capability) { 1639 return (prepareCapabilities & capability) != 0 1640 && (prepareCapabilities & ~capability) != 0; 1641 } 1642 } 1643 } 1644