1 /* 2 * Copyright (C) 2021 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 static android.os.VibrationEffect.VibrationParameter.targetAmplitude; 20 import static android.os.VibrationEffect.VibrationParameter.targetFrequency; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.app.ActivityManager; 25 import android.app.AppOpsManager; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.pm.PackageManager; 31 import android.hardware.vibrator.IVibrator; 32 import android.os.BatteryStats; 33 import android.os.Binder; 34 import android.os.Build; 35 import android.os.CombinedVibration; 36 import android.os.ExternalVibration; 37 import android.os.Handler; 38 import android.os.IBinder; 39 import android.os.IExternalVibratorService; 40 import android.os.IVibratorManagerService; 41 import android.os.IVibratorStateListener; 42 import android.os.Looper; 43 import android.os.PowerManager; 44 import android.os.Process; 45 import android.os.RemoteException; 46 import android.os.ResultReceiver; 47 import android.os.ServiceManager; 48 import android.os.ShellCallback; 49 import android.os.ShellCommand; 50 import android.os.SystemClock; 51 import android.os.Trace; 52 import android.os.VibrationAttributes; 53 import android.os.VibrationEffect; 54 import android.os.VibratorInfo; 55 import android.os.vibrator.PrebakedSegment; 56 import android.os.vibrator.VibrationEffectSegment; 57 import android.text.TextUtils; 58 import android.util.Slog; 59 import android.util.SparseArray; 60 import android.util.proto.ProtoOutputStream; 61 import android.view.Display; 62 63 import com.android.internal.annotations.GuardedBy; 64 import com.android.internal.annotations.VisibleForTesting; 65 import com.android.internal.app.IBatteryStats; 66 import com.android.internal.util.DumpUtils; 67 import com.android.internal.util.FrameworkStatsLog; 68 import com.android.server.SystemService; 69 70 import libcore.util.NativeAllocationRegistry; 71 72 import java.io.FileDescriptor; 73 import java.io.PrintWriter; 74 import java.lang.ref.WeakReference; 75 import java.time.Duration; 76 import java.util.ArrayList; 77 import java.util.Arrays; 78 import java.util.LinkedList; 79 import java.util.List; 80 import java.util.function.Consumer; 81 import java.util.function.Function; 82 83 /** System implementation of {@link IVibratorManagerService}. */ 84 public class VibratorManagerService extends IVibratorManagerService.Stub { 85 private static final String TAG = "VibratorManagerService"; 86 private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service"; 87 private static final boolean DEBUG = false; 88 private static final VibrationAttributes DEFAULT_ATTRIBUTES = 89 new VibrationAttributes.Builder().build(); 90 private static final int ATTRIBUTES_ALL_BYPASS_FLAGS = 91 VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY 92 | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF; 93 94 /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */ 95 private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000; 96 97 /** 98 * Maximum millis to wait for a vibration thread cancellation to "clean up" and finish, when 99 * blocking for an external vibration. In practice, this should be plenty. 100 */ 101 private static final long VIBRATION_CANCEL_WAIT_MILLIS = 5000; 102 103 /** Lifecycle responsible for initializing this class at the right system server phases. */ 104 public static class Lifecycle extends SystemService { 105 private VibratorManagerService mService; 106 Lifecycle(Context context)107 public Lifecycle(Context context) { 108 super(context); 109 } 110 111 @Override onStart()112 public void onStart() { 113 mService = new VibratorManagerService(getContext(), new Injector()); 114 publishBinderService(Context.VIBRATOR_MANAGER_SERVICE, mService); 115 } 116 117 @Override onBootPhase(int phase)118 public void onBootPhase(int phase) { 119 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 120 mService.systemReady(); 121 } 122 } 123 } 124 125 private final Object mLock = new Object(); 126 private final Context mContext; 127 private final PowerManager.WakeLock mWakeLock; 128 private final IBatteryStats mBatteryStatsService; 129 private final VibratorFrameworkStatsLogger mFrameworkStatsLogger; 130 private final Handler mHandler; 131 private final VibrationThread mVibrationThread; 132 private final AppOpsManager mAppOps; 133 private final NativeWrapper mNativeWrapper; 134 private final VibratorManagerRecords mVibratorManagerRecords; 135 private final long mCapabilities; 136 private final int[] mVibratorIds; 137 private final SparseArray<VibratorController> mVibrators; 138 private final VibrationThreadCallbacks mVibrationThreadCallbacks = 139 new VibrationThreadCallbacks(); 140 @GuardedBy("mLock") 141 private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>(); 142 @GuardedBy("mLock") 143 private VibrationStepConductor mCurrentVibration; 144 @GuardedBy("mLock") 145 private VibrationStepConductor mNextVibration; 146 @GuardedBy("mLock") 147 private ExternalVibrationHolder mCurrentExternalVibration; 148 @GuardedBy("mLock") 149 private boolean mServiceReady; 150 151 private final VibrationSettings mVibrationSettings; 152 private final VibrationScaler mVibrationScaler; 153 private final InputDeviceDelegate mInputDeviceDelegate; 154 private final DeviceVibrationEffectAdapter mDeviceVibrationEffectAdapter; 155 156 private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 157 @Override 158 public void onReceive(Context context, Intent intent) { 159 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 160 synchronized (mLock) { 161 // When the system is entering a non-interactive state, we want to cancel 162 // vibrations in case a misbehaving app has abandoned them. 163 if (shouldCancelOnScreenOffLocked(mNextVibration)) { 164 clearNextVibrationLocked( 165 new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF)); 166 } 167 if (shouldCancelOnScreenOffLocked(mCurrentVibration)) { 168 mCurrentVibration.notifyCancelled( 169 new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SCREEN_OFF), 170 /* immediate= */ false); 171 } 172 } 173 } 174 } 175 }; 176 nativeInit(OnSyncedVibrationCompleteListener listener)177 static native long nativeInit(OnSyncedVibrationCompleteListener listener); 178 nativeGetFinalizer()179 static native long nativeGetFinalizer(); 180 nativeGetCapabilities(long nativeServicePtr)181 static native long nativeGetCapabilities(long nativeServicePtr); 182 nativeGetVibratorIds(long nativeServicePtr)183 static native int[] nativeGetVibratorIds(long nativeServicePtr); 184 nativePrepareSynced(long nativeServicePtr, int[] vibratorIds)185 static native boolean nativePrepareSynced(long nativeServicePtr, int[] vibratorIds); 186 nativeTriggerSynced(long nativeServicePtr, long vibrationId)187 static native boolean nativeTriggerSynced(long nativeServicePtr, long vibrationId); 188 nativeCancelSynced(long nativeServicePtr)189 static native void nativeCancelSynced(long nativeServicePtr); 190 191 @VisibleForTesting VibratorManagerService(Context context, Injector injector)192 VibratorManagerService(Context context, Injector injector) { 193 mContext = context; 194 mHandler = injector.createHandler(Looper.myLooper()); 195 196 mVibrationSettings = new VibrationSettings(mContext, mHandler); 197 mVibrationScaler = new VibrationScaler(mContext, mVibrationSettings); 198 mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler); 199 mDeviceVibrationEffectAdapter = new DeviceVibrationEffectAdapter(mVibrationSettings); 200 201 VibrationCompleteListener listener = new VibrationCompleteListener(this); 202 mNativeWrapper = injector.getNativeWrapper(); 203 mNativeWrapper.init(listener); 204 205 int dumpLimit = mContext.getResources().getInteger( 206 com.android.internal.R.integer.config_previousVibrationsDumpLimit); 207 mVibratorManagerRecords = new VibratorManagerRecords(dumpLimit); 208 209 mBatteryStatsService = injector.getBatteryStatsService(); 210 mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler); 211 212 mAppOps = mContext.getSystemService(AppOpsManager.class); 213 214 PowerManager pm = context.getSystemService(PowerManager.class); 215 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); 216 mWakeLock.setReferenceCounted(true); 217 mVibrationThread = new VibrationThread(mWakeLock, mVibrationThreadCallbacks); 218 mVibrationThread.start(); 219 220 // Load vibrator hardware info. The vibrator ids and manager capabilities are loaded only 221 // once and assumed unchanged for the lifecycle of this service. Each individual vibrator 222 // can still retry loading each individual vibrator hardware spec once more at systemReady. 223 mCapabilities = mNativeWrapper.getCapabilities(); 224 int[] vibratorIds = mNativeWrapper.getVibratorIds(); 225 if (vibratorIds == null) { 226 mVibratorIds = new int[0]; 227 mVibrators = new SparseArray<>(0); 228 } else { 229 // Keep original vibrator id order, which might be meaningful. 230 mVibratorIds = vibratorIds; 231 mVibrators = new SparseArray<>(mVibratorIds.length); 232 for (int vibratorId : vibratorIds) { 233 mVibrators.put(vibratorId, injector.createVibratorController(vibratorId, listener)); 234 } 235 } 236 237 // Reset the hardware to a default state, in case this is a runtime restart instead of a 238 // fresh boot. 239 mNativeWrapper.cancelSynced(); 240 for (int i = 0; i < mVibrators.size(); i++) { 241 mVibrators.valueAt(i).reset(); 242 } 243 244 IntentFilter filter = new IntentFilter(); 245 filter.addAction(Intent.ACTION_SCREEN_OFF); 246 context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED); 247 248 injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); 249 } 250 251 /** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */ 252 @VisibleForTesting systemReady()253 void systemReady() { 254 Slog.v(TAG, "Initializing VibratorManager service..."); 255 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "systemReady"); 256 try { 257 // Will retry to load each vibrator's info, if any request have failed. 258 for (int i = 0; i < mVibrators.size(); i++) { 259 mVibrators.valueAt(i).reloadVibratorInfoIfNeeded(); 260 } 261 262 mVibrationSettings.onSystemReady(); 263 mInputDeviceDelegate.onSystemReady(); 264 265 mVibrationSettings.addListener(this::updateServiceState); 266 267 // Will update settings and input devices. 268 updateServiceState(); 269 } finally { 270 synchronized (mLock) { 271 mServiceReady = true; 272 } 273 Slog.v(TAG, "VibratorManager service initialized"); 274 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 275 } 276 } 277 278 @Override // Binder call getVibratorIds()279 public int[] getVibratorIds() { 280 return Arrays.copyOf(mVibratorIds, mVibratorIds.length); 281 } 282 283 @Override // Binder call 284 @Nullable getVibratorInfo(int vibratorId)285 public VibratorInfo getVibratorInfo(int vibratorId) { 286 final VibratorController controller = mVibrators.get(vibratorId); 287 if (controller == null) { 288 return null; 289 } 290 final VibratorInfo info = controller.getVibratorInfo(); 291 synchronized (mLock) { 292 if (mServiceReady) { 293 return info; 294 } 295 } 296 // If the service is not ready and the load was unsuccessful then return null while waiting 297 // for the service to be ready. It will retry to load the complete info from the HAL. 298 return controller.isVibratorInfoLoadSuccessful() ? info : null; 299 } 300 301 @Override // Binder call isVibrating(int vibratorId)302 public boolean isVibrating(int vibratorId) { 303 mContext.enforceCallingOrSelfPermission( 304 android.Manifest.permission.ACCESS_VIBRATOR_STATE, 305 "isVibrating"); 306 VibratorController controller = mVibrators.get(vibratorId); 307 return controller != null && controller.isVibrating(); 308 } 309 310 @Override // Binder call registerVibratorStateListener(int vibratorId, IVibratorStateListener listener)311 public boolean registerVibratorStateListener(int vibratorId, IVibratorStateListener listener) { 312 mContext.enforceCallingOrSelfPermission( 313 android.Manifest.permission.ACCESS_VIBRATOR_STATE, 314 "registerVibratorStateListener"); 315 VibratorController controller = mVibrators.get(vibratorId); 316 if (controller == null) { 317 return false; 318 } 319 return controller.registerVibratorStateListener(listener); 320 } 321 322 @Override // Binder call unregisterVibratorStateListener(int vibratorId, IVibratorStateListener listener)323 public boolean unregisterVibratorStateListener(int vibratorId, 324 IVibratorStateListener listener) { 325 mContext.enforceCallingOrSelfPermission( 326 android.Manifest.permission.ACCESS_VIBRATOR_STATE, 327 "unregisterVibratorStateListener"); 328 VibratorController controller = mVibrators.get(vibratorId); 329 if (controller == null) { 330 return false; 331 } 332 return controller.unregisterVibratorStateListener(listener); 333 } 334 335 @Override // Binder call setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable CombinedVibration effect, @Nullable VibrationAttributes attrs)336 public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, 337 @Nullable CombinedVibration effect, @Nullable VibrationAttributes attrs) { 338 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "setAlwaysOnEffect"); 339 try { 340 mContext.enforceCallingOrSelfPermission( 341 android.Manifest.permission.VIBRATE_ALWAYS_ON, 342 "setAlwaysOnEffect"); 343 344 if (effect == null) { 345 synchronized (mLock) { 346 mAlwaysOnEffects.delete(alwaysOnId); 347 onAllVibratorsLocked(v -> { 348 if (v.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { 349 v.updateAlwaysOn(alwaysOnId, /* effect= */ null); 350 } 351 }); 352 } 353 return true; 354 } 355 if (!isEffectValid(effect)) { 356 return false; 357 } 358 attrs = fixupVibrationAttributes(attrs, effect); 359 synchronized (mLock) { 360 SparseArray<PrebakedSegment> effects = fixupAlwaysOnEffectsLocked(effect); 361 if (effects == null) { 362 // Invalid effects set in CombinedVibrationEffect, or always-on capability is 363 // missing on individual vibrators. 364 return false; 365 } 366 AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId, 367 new Vibration.CallerInfo(attrs, uid, Display.DEFAULT_DISPLAY, opPkg, 368 null), effects); 369 mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration); 370 updateAlwaysOnLocked(alwaysOnVibration); 371 } 372 return true; 373 } finally { 374 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 375 } 376 } 377 378 @Override // Binder call vibrate(int uid, int displayId, String opPkg, @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, String reason, IBinder token)379 public void vibrate(int uid, int displayId, String opPkg, @NonNull CombinedVibration effect, 380 @Nullable VibrationAttributes attrs, String reason, IBinder token) { 381 vibrateInternal(uid, displayId, opPkg, effect, attrs, reason, token); 382 } 383 384 /** 385 * An internal-only version of vibrate that allows the caller access to the 386 * {@link HalVibration}. 387 * The Vibration is only returned if it is ongoing after this method returns. 388 */ 389 @VisibleForTesting 390 @Nullable vibrateInternal(int uid, int displayId, String opPkg, @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, String reason, IBinder token)391 HalVibration vibrateInternal(int uid, int displayId, String opPkg, 392 @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, 393 String reason, IBinder token) { 394 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason); 395 try { 396 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.VIBRATE, "vibrate"); 397 398 if (token == null) { 399 Slog.e(TAG, "token must not be null"); 400 return null; 401 } 402 enforceUpdateAppOpsStatsPermission(uid); 403 if (!isEffectValid(effect)) { 404 return null; 405 } 406 attrs = fixupVibrationAttributes(attrs, effect); 407 // Create Vibration.Stats as close to the received request as possible, for tracking. 408 HalVibration vib = new HalVibration(token, effect, 409 new Vibration.CallerInfo(attrs, uid, displayId, opPkg, reason)); 410 fillVibrationFallbacks(vib, effect); 411 412 if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) { 413 // Force update of user settings before checking if this vibration effect should 414 // be ignored or scaled. 415 mVibrationSettings.mSettingObserver.onChange(false); 416 } 417 418 synchronized (mLock) { 419 if (DEBUG) { 420 Slog.d(TAG, "Starting vibrate for vibration " + vib.id); 421 } 422 423 // Check if user settings or DnD is set to ignore this vibration. 424 Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(vib.callerInfo); 425 426 // Check if ongoing vibration is more important than this vibration. 427 if (vibrationEndInfo == null) { 428 vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(vib); 429 } 430 431 // If not ignored so far then try to start this vibration. 432 if (vibrationEndInfo == null) { 433 final long ident = Binder.clearCallingIdentity(); 434 try { 435 if (mCurrentExternalVibration != null) { 436 mCurrentExternalVibration.mute(); 437 vib.stats.reportInterruptedAnotherVibration( 438 mCurrentExternalVibration.callerInfo); 439 endExternalVibrateLocked( 440 new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED, 441 vib.callerInfo), 442 /* continueExternalControl= */ false); 443 } else if (mCurrentVibration != null) { 444 if (mCurrentVibration.getVibration().canPipelineWith(vib)) { 445 // Don't cancel the current vibration if it's pipeline-able. 446 // Note that if there is a pending next vibration that can't be 447 // pipelined, it will have already cancelled the current one, so we 448 // don't need to consider it here as well. 449 if (DEBUG) { 450 Slog.d(TAG, "Pipelining vibration " + vib.id); 451 } 452 } else { 453 vib.stats.reportInterruptedAnotherVibration( 454 mCurrentVibration.getVibration().callerInfo); 455 mCurrentVibration.notifyCancelled( 456 new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED, 457 vib.callerInfo), 458 /* immediate= */ false); 459 } 460 } 461 vibrationEndInfo = startVibrationLocked(vib); 462 } finally { 463 Binder.restoreCallingIdentity(ident); 464 } 465 } 466 467 // Ignored or failed to start the vibration, end it and report metrics right away. 468 if (vibrationEndInfo != null) { 469 endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ true); 470 } 471 return vib; 472 } 473 } finally { 474 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 475 } 476 } 477 478 @Override // Binder call cancelVibrate(int usageFilter, IBinder token)479 public void cancelVibrate(int usageFilter, IBinder token) { 480 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "cancelVibrate"); 481 try { 482 mContext.enforceCallingOrSelfPermission( 483 android.Manifest.permission.VIBRATE, 484 "cancelVibrate"); 485 486 synchronized (mLock) { 487 if (DEBUG) { 488 Slog.d(TAG, "Canceling vibration"); 489 } 490 Vibration.EndInfo cancelledByUserInfo = 491 new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_USER); 492 final long ident = Binder.clearCallingIdentity(); 493 try { 494 if (mNextVibration != null 495 && shouldCancelVibration(mNextVibration.getVibration(), 496 usageFilter, token)) { 497 clearNextVibrationLocked(cancelledByUserInfo); 498 } 499 if (mCurrentVibration != null 500 && shouldCancelVibration(mCurrentVibration.getVibration(), 501 usageFilter, token)) { 502 mCurrentVibration.notifyCancelled( 503 cancelledByUserInfo, /* immediate= */false); 504 } 505 if (mCurrentExternalVibration != null 506 && shouldCancelVibration( 507 mCurrentExternalVibration.externalVibration.getVibrationAttributes(), 508 usageFilter)) { 509 mCurrentExternalVibration.mute(); 510 endExternalVibrateLocked( 511 cancelledByUserInfo, /* continueExternalControl= */ false); 512 } 513 } finally { 514 Binder.restoreCallingIdentity(ident); 515 } 516 } 517 } finally { 518 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 519 } 520 } 521 522 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)523 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 524 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 525 526 final long ident = Binder.clearCallingIdentity(); 527 528 boolean isDumpProto = false; 529 for (String arg : args) { 530 if (arg.equals("--proto")) { 531 isDumpProto = true; 532 } 533 } 534 try { 535 if (isDumpProto) { 536 dumpProto(fd); 537 } else { 538 dumpText(pw); 539 } 540 } finally { 541 Binder.restoreCallingIdentity(ident); 542 } 543 } 544 dumpText(PrintWriter pw)545 private void dumpText(PrintWriter pw) { 546 if (DEBUG) { 547 Slog.d(TAG, "Dumping vibrator manager service to text..."); 548 } 549 synchronized (mLock) { 550 pw.println("Vibrator Manager Service:"); 551 pw.println(" mVibrationSettings:"); 552 pw.println(" " + mVibrationSettings); 553 pw.println(); 554 pw.println(" mVibratorControllers:"); 555 for (int i = 0; i < mVibrators.size(); i++) { 556 pw.println(" " + mVibrators.valueAt(i)); 557 } 558 pw.println(); 559 pw.println(" mCurrentVibration:"); 560 pw.println(" " + (mCurrentVibration == null 561 ? null : mCurrentVibration.getVibration().getDebugInfo())); 562 pw.println(); 563 pw.println(" mNextVibration:"); 564 pw.println(" " + (mNextVibration == null 565 ? null : mNextVibration.getVibration().getDebugInfo())); 566 pw.println(); 567 pw.println(" mCurrentExternalVibration:"); 568 pw.println(" " + (mCurrentExternalVibration == null 569 ? null : mCurrentExternalVibration.getDebugInfo())); 570 pw.println(); 571 } 572 mVibratorManagerRecords.dumpText(pw); 573 } 574 dumpProto(FileDescriptor fd)575 synchronized void dumpProto(FileDescriptor fd) { 576 final ProtoOutputStream proto = new ProtoOutputStream(fd); 577 if (DEBUG) { 578 Slog.d(TAG, "Dumping vibrator manager service to proto..."); 579 } 580 synchronized (mLock) { 581 mVibrationSettings.dumpProto(proto); 582 if (mCurrentVibration != null) { 583 mCurrentVibration.getVibration().getDebugInfo().dumpProto(proto, 584 VibratorManagerServiceDumpProto.CURRENT_VIBRATION); 585 } 586 if (mCurrentExternalVibration != null) { 587 mCurrentExternalVibration.getDebugInfo().dumpProto(proto, 588 VibratorManagerServiceDumpProto.CURRENT_EXTERNAL_VIBRATION); 589 } 590 591 boolean isVibrating = false; 592 boolean isUnderExternalControl = false; 593 for (int i = 0; i < mVibrators.size(); i++) { 594 proto.write(VibratorManagerServiceDumpProto.VIBRATOR_IDS, mVibrators.keyAt(i)); 595 isVibrating |= mVibrators.valueAt(i).isVibrating(); 596 isUnderExternalControl |= mVibrators.valueAt(i).isUnderExternalControl(); 597 } 598 proto.write(VibratorManagerServiceDumpProto.IS_VIBRATING, isVibrating); 599 proto.write(VibratorManagerServiceDumpProto.VIBRATOR_UNDER_EXTERNAL_CONTROL, 600 isUnderExternalControl); 601 } 602 mVibratorManagerRecords.dumpProto(proto); 603 proto.flush(); 604 } 605 606 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback cb, ResultReceiver resultReceiver)607 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 608 String[] args, ShellCallback cb, ResultReceiver resultReceiver) { 609 new VibratorManagerShellCommand(cb.getShellCallbackBinder()) 610 .exec(this, in, out, err, args, cb, resultReceiver); 611 } 612 613 @VisibleForTesting updateServiceState()614 void updateServiceState() { 615 synchronized (mLock) { 616 if (DEBUG) { 617 Slog.d(TAG, "Updating device state..."); 618 } 619 boolean inputDevicesChanged = mInputDeviceDelegate.updateInputDeviceVibrators( 620 mVibrationSettings.shouldVibrateInputDevices()); 621 622 for (int i = 0; i < mAlwaysOnEffects.size(); i++) { 623 updateAlwaysOnLocked(mAlwaysOnEffects.valueAt(i)); 624 } 625 626 if (mCurrentVibration == null) { 627 return; 628 } 629 630 HalVibration vib = mCurrentVibration.getVibration(); 631 Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(vib.callerInfo); 632 633 if (inputDevicesChanged || (vibrationEndInfo != null)) { 634 if (DEBUG) { 635 Slog.d(TAG, "Canceling vibration because settings changed: " 636 + (inputDevicesChanged ? "input devices changed" 637 : vibrationEndInfo.status)); 638 } 639 mCurrentVibration.notifyCancelled( 640 new Vibration.EndInfo(Vibration.Status.CANCELLED_BY_SETTINGS_UPDATE), 641 /* immediate= */ false); 642 } 643 } 644 } 645 setExternalControl(boolean externalControl, VibrationStats vibrationStats)646 private void setExternalControl(boolean externalControl, VibrationStats vibrationStats) { 647 for (int i = 0; i < mVibrators.size(); i++) { 648 mVibrators.valueAt(i).setExternalControl(externalControl); 649 vibrationStats.reportSetExternalControl(); 650 } 651 } 652 653 @GuardedBy("mLock") updateAlwaysOnLocked(AlwaysOnVibration vib)654 private void updateAlwaysOnLocked(AlwaysOnVibration vib) { 655 for (int i = 0; i < vib.effects.size(); i++) { 656 VibratorController vibrator = mVibrators.get(vib.effects.keyAt(i)); 657 PrebakedSegment effect = vib.effects.valueAt(i); 658 if (vibrator == null) { 659 continue; 660 } 661 Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked(vib.callerInfo); 662 if (vibrationEndInfo == null) { 663 effect = mVibrationScaler.scale(effect, vib.callerInfo.attrs.getUsage()); 664 } else { 665 // Vibration should not run, use null effect to remove registered effect. 666 effect = null; 667 } 668 vibrator.updateAlwaysOn(vib.alwaysOnId, effect); 669 } 670 } 671 672 @GuardedBy("mLock") 673 @Nullable startVibrationLocked(HalVibration vib)674 private Vibration.EndInfo startVibrationLocked(HalVibration vib) { 675 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked"); 676 try { 677 vib.updateEffects( 678 effect -> mVibrationScaler.scale(effect, vib.callerInfo.attrs.getUsage())); 679 boolean inputDevicesAvailable = mInputDeviceDelegate.vibrateIfAvailable( 680 vib.callerInfo, vib.getEffect()); 681 if (inputDevicesAvailable) { 682 return new Vibration.EndInfo(Vibration.Status.FORWARDED_TO_INPUT_DEVICES); 683 } 684 685 VibrationStepConductor conductor = new VibrationStepConductor(vib, mVibrationSettings, 686 mDeviceVibrationEffectAdapter, mVibrators, mVibrationThreadCallbacks); 687 if (mCurrentVibration == null) { 688 return startVibrationOnThreadLocked(conductor); 689 } 690 // If there's already a vibration queued (waiting for the previous one to finish 691 // cancelling), end it cleanly and replace it with the new one. 692 // Note that we don't consider pipelining here, because new pipelined ones should 693 // replace pending non-executing pipelined ones anyway. 694 clearNextVibrationLocked( 695 new Vibration.EndInfo(Vibration.Status.IGNORED_SUPERSEDED, vib.callerInfo)); 696 mNextVibration = conductor; 697 return null; 698 } finally { 699 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 700 } 701 } 702 703 @GuardedBy("mLock") 704 @Nullable startVibrationOnThreadLocked(VibrationStepConductor conductor)705 private Vibration.EndInfo startVibrationOnThreadLocked(VibrationStepConductor conductor) { 706 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationThreadLocked"); 707 try { 708 HalVibration vib = conductor.getVibration(); 709 int mode = startAppOpModeLocked(vib.callerInfo); 710 switch (mode) { 711 case AppOpsManager.MODE_ALLOWED: 712 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); 713 // Make sure mCurrentVibration is set while triggering the VibrationThread. 714 mCurrentVibration = conductor; 715 if (!mVibrationThread.runVibrationOnVibrationThread(mCurrentVibration)) { 716 // Shouldn't happen. The method call already logs a wtf. 717 mCurrentVibration = null; // Aborted. 718 return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_SCHEDULING); 719 } 720 return null; 721 case AppOpsManager.MODE_ERRORED: 722 Slog.w(TAG, "Start AppOpsManager operation errored for uid " 723 + vib.callerInfo.uid); 724 return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS); 725 default: 726 return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS); 727 } 728 } finally { 729 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 730 } 731 } 732 733 @GuardedBy("mLock") endVibrationLocked(HalVibration vib, Vibration.EndInfo vibrationEndInfo, boolean shouldWriteStats)734 private void endVibrationLocked(HalVibration vib, Vibration.EndInfo vibrationEndInfo, 735 boolean shouldWriteStats) { 736 vib.end(vibrationEndInfo); 737 logVibrationStatus(vib.callerInfo.uid, vib.callerInfo.attrs, 738 vibrationEndInfo.status); 739 mVibratorManagerRecords.record(vib); 740 if (shouldWriteStats) { 741 mFrameworkStatsLogger.writeVibrationReportedAsync( 742 vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis())); 743 } 744 } 745 746 @GuardedBy("mLock") endVibrationAndWriteStatsLocked(ExternalVibrationHolder vib, Vibration.EndInfo vibrationEndInfo)747 private void endVibrationAndWriteStatsLocked(ExternalVibrationHolder vib, 748 Vibration.EndInfo vibrationEndInfo) { 749 vib.end(vibrationEndInfo); 750 logVibrationStatus(vib.externalVibration.getUid(), 751 vib.externalVibration.getVibrationAttributes(), vibrationEndInfo.status); 752 mVibratorManagerRecords.record(vib); 753 mFrameworkStatsLogger.writeVibrationReportedAsync( 754 vib.getStatsInfo(/* completionUptimeMillis= */ SystemClock.uptimeMillis())); 755 } 756 logVibrationStatus(int uid, VibrationAttributes attrs, Vibration.Status status)757 private void logVibrationStatus(int uid, VibrationAttributes attrs, 758 Vibration.Status status) { 759 switch (status) { 760 case IGNORED_BACKGROUND: 761 Slog.e(TAG, "Ignoring incoming vibration as process with" 762 + " uid= " + uid + " is background," + " attrs= " + attrs); 763 break; 764 case IGNORED_ERROR_APP_OPS: 765 Slog.w(TAG, "Would be an error: vibrate from uid " + uid); 766 break; 767 case IGNORED_FOR_EXTERNAL: 768 if (DEBUG) { 769 Slog.d(TAG, "Ignoring incoming vibration for current external vibration"); 770 } 771 break; 772 case IGNORED_FOR_HIGHER_IMPORTANCE: 773 if (DEBUG) { 774 Slog.d(TAG, "Ignoring incoming vibration in favor of ongoing vibration" 775 + " with higher importance"); 776 } 777 break; 778 case IGNORED_FOR_ONGOING: 779 if (DEBUG) { 780 Slog.d(TAG, "Ignoring incoming vibration in favor of repeating vibration"); 781 } 782 break; 783 case IGNORED_FOR_RINGER_MODE: 784 if (DEBUG) { 785 Slog.d(TAG, "Ignoring incoming vibration because of ringer mode, attrs=" 786 + attrs); 787 } 788 break; 789 case IGNORED_FROM_VIRTUAL_DEVICE: 790 if (DEBUG) { 791 Slog.d(TAG, "Ignoring incoming vibration because it came from a virtual" 792 + " device, attrs= " + attrs); 793 } 794 break; 795 default: 796 if (DEBUG) { 797 Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs 798 + " ended with status " + status); 799 } 800 } 801 } 802 803 @GuardedBy("mLock") reportFinishedVibrationLocked(Vibration.EndInfo vibrationEndInfo)804 private void reportFinishedVibrationLocked(Vibration.EndInfo vibrationEndInfo) { 805 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked"); 806 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); 807 try { 808 HalVibration vib = mCurrentVibration.getVibration(); 809 if (DEBUG) { 810 Slog.d(TAG, "Reporting vibration " + vib.id + " finished with " 811 + vibrationEndInfo); 812 } 813 // DO NOT write metrics at this point, wait for the VibrationThread to report the 814 // vibration was released, after all cleanup. The metrics will be reported then. 815 endVibrationLocked(vib, vibrationEndInfo, /* shouldWriteStats= */ false); 816 finishAppOpModeLocked(vib.callerInfo); 817 } finally { 818 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 819 } 820 } 821 onSyncedVibrationComplete(long vibrationId)822 private void onSyncedVibrationComplete(long vibrationId) { 823 synchronized (mLock) { 824 if (mCurrentVibration != null 825 && mCurrentVibration.getVibration().id == vibrationId) { 826 if (DEBUG) { 827 Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread"); 828 } 829 mCurrentVibration.notifySyncedVibrationComplete(); 830 } 831 } 832 } 833 onVibrationComplete(int vibratorId, long vibrationId)834 private void onVibrationComplete(int vibratorId, long vibrationId) { 835 synchronized (mLock) { 836 if (mCurrentVibration != null 837 && mCurrentVibration.getVibration().id == vibrationId) { 838 if (DEBUG) { 839 Slog.d(TAG, "Vibration " + vibrationId + " on vibrator " + vibratorId 840 + " complete, notifying thread"); 841 } 842 mCurrentVibration.notifyVibratorComplete(vibratorId); 843 } 844 } 845 } 846 847 /** 848 * Check if given vibration should be ignored by this service because of the ongoing vibration. 849 * 850 * @return a Vibration.EndInfo if the vibration should be ignored, null otherwise. 851 */ 852 @GuardedBy("mLock") 853 @Nullable shouldIgnoreVibrationForOngoingLocked(Vibration vib)854 private Vibration.EndInfo shouldIgnoreVibrationForOngoingLocked(Vibration vib) { 855 if (mCurrentExternalVibration != null) { 856 return shouldIgnoreVibrationForOngoing(vib, mCurrentExternalVibration); 857 } 858 859 if (mNextVibration != null) { 860 Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationForOngoing(vib, 861 mNextVibration.getVibration()); 862 if (vibrationEndInfo != null) { 863 // Next vibration has higher importance than the new one, so the new vibration 864 // should be ignored. 865 return vibrationEndInfo; 866 } 867 } 868 869 if (mCurrentVibration != null) { 870 HalVibration currentVibration = mCurrentVibration.getVibration(); 871 if (currentVibration.hasEnded() || mCurrentVibration.wasNotifiedToCancel()) { 872 // Current vibration has ended or is cancelling, should not block incoming 873 // vibrations. 874 return null; 875 } 876 877 return shouldIgnoreVibrationForOngoing(vib, currentVibration); 878 } 879 880 return null; 881 } 882 883 /** 884 * Checks if the ongoing vibration has higher importance than the new one. If they have similar 885 * importance, then {@link Vibration#isRepeating()} is used as a tiebreaker. 886 * 887 * @return a Vibration.EndInfo if the vibration should be ignored, null otherwise. 888 */ 889 @Nullable shouldIgnoreVibrationForOngoing( @onNull Vibration newVibration, @NonNull Vibration ongoingVibration)890 private static Vibration.EndInfo shouldIgnoreVibrationForOngoing( 891 @NonNull Vibration newVibration, @NonNull Vibration ongoingVibration) { 892 893 int newVibrationImportance = getVibrationImportance(newVibration); 894 int ongoingVibrationImportance = getVibrationImportance(ongoingVibration); 895 896 if (newVibrationImportance > ongoingVibrationImportance) { 897 // New vibration has higher importance and should not be ignored. 898 return null; 899 } 900 901 if (ongoingVibrationImportance > newVibrationImportance) { 902 // Existing vibration has higher importance and should not be cancelled. 903 return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_HIGHER_IMPORTANCE, 904 ongoingVibration.callerInfo); 905 } 906 907 // Same importance, use repeating as a tiebreaker. 908 if (ongoingVibration.isRepeating() && !newVibration.isRepeating()) { 909 // Ongoing vibration is repeating and new one is not, give priority to ongoing 910 return new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_ONGOING, 911 ongoingVibration.callerInfo); 912 } 913 // New vibration is repeating or this is a complete tie between them, 914 // give priority to new vibration. 915 return null; 916 } 917 918 /** 919 * Gets the vibration importance based on usage. In the case where usage is unknown, it maps 920 * repeating vibrations to ringtones and non-repeating vibrations to touches. 921 * 922 * @return a numeric representation for the vibration importance, larger values represent a 923 * higher importance 924 */ getVibrationImportance(Vibration vibration)925 private static int getVibrationImportance(Vibration vibration) { 926 int usage = vibration.callerInfo.attrs.getUsage(); 927 if (usage == VibrationAttributes.USAGE_UNKNOWN) { 928 if (vibration.isRepeating()) { 929 usage = VibrationAttributes.USAGE_RINGTONE; 930 } else { 931 usage = VibrationAttributes.USAGE_TOUCH; 932 } 933 } 934 935 switch (usage) { 936 case VibrationAttributes.USAGE_RINGTONE: 937 return 5; 938 case VibrationAttributes.USAGE_ALARM: 939 return 4; 940 case VibrationAttributes.USAGE_NOTIFICATION: 941 return 3; 942 case VibrationAttributes.USAGE_COMMUNICATION_REQUEST: 943 case VibrationAttributes.USAGE_ACCESSIBILITY: 944 return 2; 945 case VibrationAttributes.USAGE_HARDWARE_FEEDBACK: 946 case VibrationAttributes.USAGE_PHYSICAL_EMULATION: 947 return 1; 948 case VibrationAttributes.USAGE_MEDIA: 949 case VibrationAttributes.USAGE_TOUCH: 950 default: 951 return 0; 952 } 953 } 954 955 /** 956 * Check if given vibration should be ignored by this service. 957 * 958 * @return a Vibration.EndInfo if the vibration should be ignored, null otherwise. 959 */ 960 @GuardedBy("mLock") 961 @Nullable shouldIgnoreVibrationLocked(Vibration.CallerInfo callerInfo)962 private Vibration.EndInfo shouldIgnoreVibrationLocked(Vibration.CallerInfo callerInfo) { 963 Vibration.Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo); 964 if (statusFromSettings != null) { 965 return new Vibration.EndInfo(statusFromSettings); 966 } 967 968 int mode = checkAppOpModeLocked(callerInfo); 969 if (mode != AppOpsManager.MODE_ALLOWED) { 970 if (mode == AppOpsManager.MODE_ERRORED) { 971 // We might be getting calls from within system_server, so we don't actually 972 // want to throw a SecurityException here. 973 return new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_APP_OPS); 974 } else { 975 return new Vibration.EndInfo(Vibration.Status.IGNORED_APP_OPS); 976 } 977 } 978 979 return null; 980 } 981 982 /** 983 * Return true if the vibration has the same token and usage belongs to given usage class. 984 * 985 * @param vib The ongoing or pending vibration to be cancelled. 986 * @param usageFilter The vibration usages to be cancelled, any bitwise combination of 987 * VibrationAttributes.USAGE_* values. 988 * @param token The binder token to identify the vibration origin. Only vibrations 989 * started with the same token can be cancelled with it. 990 */ shouldCancelVibration(HalVibration vib, int usageFilter, IBinder token)991 private boolean shouldCancelVibration(HalVibration vib, int usageFilter, IBinder token) { 992 return (vib.callerToken == token) && shouldCancelVibration(vib.callerInfo.attrs, 993 usageFilter); 994 } 995 996 /** 997 * Return true if the external vibration usage belongs to given usage class. 998 * 999 * @param attrs The attributes of an ongoing or pending vibration to be cancelled. 1000 * @param usageFilter The vibration usages to be cancelled, any bitwise combination of 1001 * VibrationAttributes.USAGE_* values. 1002 */ shouldCancelVibration(VibrationAttributes attrs, int usageFilter)1003 private boolean shouldCancelVibration(VibrationAttributes attrs, int usageFilter) { 1004 if (attrs.getUsage() == VibrationAttributes.USAGE_UNKNOWN) { 1005 // Special case, usage UNKNOWN would match all filters. Instead it should only match if 1006 // it's cancelling that usage specifically, or if cancelling all usages. 1007 return usageFilter == VibrationAttributes.USAGE_UNKNOWN 1008 || usageFilter == VibrationAttributes.USAGE_FILTER_MATCH_ALL; 1009 } 1010 return (usageFilter & attrs.getUsage()) == attrs.getUsage(); 1011 } 1012 1013 /** 1014 * Check which mode should be set for a vibration with given {@code uid}, {@code opPkg} and 1015 * {@code attrs}. This will return one of the AppOpsManager.MODE_*. 1016 */ 1017 @GuardedBy("mLock") checkAppOpModeLocked(Vibration.CallerInfo callerInfo)1018 private int checkAppOpModeLocked(Vibration.CallerInfo callerInfo) { 1019 int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE, 1020 callerInfo.attrs.getAudioUsage(), callerInfo.uid, callerInfo.opPkg); 1021 int fixedMode = fixupAppOpModeLocked(mode, callerInfo.attrs); 1022 if (mode != fixedMode && fixedMode == AppOpsManager.MODE_ALLOWED) { 1023 // If we're just ignoring the vibration op then this is set by DND and we should ignore 1024 // if we're asked to bypass. AppOps won't be able to record this operation, so make 1025 // sure we at least note it in the logs for debugging. 1026 Slog.d(TAG, "Bypassing DND for vibrate from uid " + callerInfo.uid); 1027 } 1028 return fixedMode; 1029 } 1030 1031 /** Start an operation in {@link AppOpsManager}, if allowed. */ 1032 @GuardedBy("mLock") startAppOpModeLocked(Vibration.CallerInfo callerInfo)1033 private int startAppOpModeLocked(Vibration.CallerInfo callerInfo) { 1034 return fixupAppOpModeLocked( 1035 mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg), 1036 callerInfo.attrs); 1037 } 1038 1039 /** 1040 * Finish a previously started operation in {@link AppOpsManager}. This will be a noop if no 1041 * operation with same uid was previously started. 1042 */ 1043 @GuardedBy("mLock") finishAppOpModeLocked(Vibration.CallerInfo callerInfo)1044 private void finishAppOpModeLocked(Vibration.CallerInfo callerInfo) { 1045 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg); 1046 } 1047 1048 /** 1049 * Enforces {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} to incoming UID if it's 1050 * different from the calling UID. 1051 */ enforceUpdateAppOpsStatsPermission(int uid)1052 private void enforceUpdateAppOpsStatsPermission(int uid) { 1053 if (uid == Binder.getCallingUid()) { 1054 return; 1055 } 1056 if (Binder.getCallingPid() == Process.myPid()) { 1057 return; 1058 } 1059 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, 1060 Binder.getCallingPid(), Binder.getCallingUid(), null); 1061 } 1062 1063 /** 1064 * Validate the incoming {@link CombinedVibration}. 1065 * 1066 * We can't throw exceptions here since we might be called from some system_server component, 1067 * which would bring the whole system down. 1068 * 1069 * @return whether the CombinedVibrationEffect is non-null and valid 1070 */ isEffectValid(@ullable CombinedVibration effect)1071 private static boolean isEffectValid(@Nullable CombinedVibration effect) { 1072 if (effect == null) { 1073 Slog.wtf(TAG, "effect must not be null"); 1074 return false; 1075 } 1076 try { 1077 effect.validate(); 1078 } catch (Exception e) { 1079 Slog.wtf(TAG, "Encountered issue when verifying CombinedVibrationEffect.", e); 1080 return false; 1081 } 1082 return true; 1083 } 1084 1085 /** 1086 * Sets fallback effects to all prebaked ones in given combination of effects, based on {@link 1087 * VibrationSettings#getFallbackEffect}. 1088 */ fillVibrationFallbacks(HalVibration vib, CombinedVibration effect)1089 private void fillVibrationFallbacks(HalVibration vib, CombinedVibration effect) { 1090 if (effect instanceof CombinedVibration.Mono) { 1091 fillVibrationFallbacks(vib, ((CombinedVibration.Mono) effect).getEffect()); 1092 } else if (effect instanceof CombinedVibration.Stereo) { 1093 SparseArray<VibrationEffect> effects = 1094 ((CombinedVibration.Stereo) effect).getEffects(); 1095 for (int i = 0; i < effects.size(); i++) { 1096 fillVibrationFallbacks(vib, effects.valueAt(i)); 1097 } 1098 } else if (effect instanceof CombinedVibration.Sequential) { 1099 List<CombinedVibration> effects = 1100 ((CombinedVibration.Sequential) effect).getEffects(); 1101 for (int i = 0; i < effects.size(); i++) { 1102 fillVibrationFallbacks(vib, effects.get(i)); 1103 } 1104 } 1105 } 1106 fillVibrationFallbacks(HalVibration vib, VibrationEffect effect)1107 private void fillVibrationFallbacks(HalVibration vib, VibrationEffect effect) { 1108 VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; 1109 int segmentCount = composed.getSegments().size(); 1110 for (int i = 0; i < segmentCount; i++) { 1111 VibrationEffectSegment segment = composed.getSegments().get(i); 1112 if (segment instanceof PrebakedSegment) { 1113 PrebakedSegment prebaked = (PrebakedSegment) segment; 1114 VibrationEffect fallback = mVibrationSettings.getFallbackEffect( 1115 prebaked.getEffectId()); 1116 if (prebaked.shouldFallback() && fallback != null) { 1117 vib.addFallback(prebaked.getEffectId(), fallback); 1118 } 1119 } 1120 } 1121 } 1122 1123 /** 1124 * Return new {@link VibrationAttributes} that only applies flags that this user has permissions 1125 * to use. 1126 */ 1127 @NonNull fixupVibrationAttributes(@ullable VibrationAttributes attrs, @Nullable CombinedVibration effect)1128 private VibrationAttributes fixupVibrationAttributes(@Nullable VibrationAttributes attrs, 1129 @Nullable CombinedVibration effect) { 1130 if (attrs == null) { 1131 attrs = DEFAULT_ATTRIBUTES; 1132 } 1133 int usage = attrs.getUsage(); 1134 if ((usage == VibrationAttributes.USAGE_UNKNOWN) 1135 && (effect != null) && effect.isHapticFeedbackCandidate()) { 1136 usage = VibrationAttributes.USAGE_TOUCH; 1137 } 1138 int flags = attrs.getFlags(); 1139 if ((flags & ATTRIBUTES_ALL_BYPASS_FLAGS) != 0) { 1140 if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) 1141 || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE) 1142 || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) { 1143 // Remove bypass flags from attributes if the app does not have permissions. 1144 flags &= ~ATTRIBUTES_ALL_BYPASS_FLAGS; 1145 } 1146 } 1147 if ((usage == attrs.getUsage()) && (flags == attrs.getFlags())) { 1148 return attrs; 1149 } 1150 return new VibrationAttributes.Builder(attrs) 1151 .setUsage(usage) 1152 .setFlags(flags, attrs.getFlags()) 1153 .build(); 1154 } 1155 1156 @GuardedBy("mLock") 1157 @Nullable fixupAlwaysOnEffectsLocked( CombinedVibration effect)1158 private SparseArray<PrebakedSegment> fixupAlwaysOnEffectsLocked( 1159 CombinedVibration effect) { 1160 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "fixupAlwaysOnEffectsLocked"); 1161 try { 1162 SparseArray<VibrationEffect> effects; 1163 if (effect instanceof CombinedVibration.Mono) { 1164 VibrationEffect syncedEffect = ((CombinedVibration.Mono) effect).getEffect(); 1165 effects = transformAllVibratorsLocked(unused -> syncedEffect); 1166 } else if (effect instanceof CombinedVibration.Stereo) { 1167 effects = ((CombinedVibration.Stereo) effect).getEffects(); 1168 } else { 1169 // Only synced combinations can be used for always-on effects. 1170 return null; 1171 } 1172 SparseArray<PrebakedSegment> result = new SparseArray<>(); 1173 for (int i = 0; i < effects.size(); i++) { 1174 PrebakedSegment prebaked = extractPrebakedSegment(effects.valueAt(i)); 1175 if (prebaked == null) { 1176 Slog.e(TAG, "Only prebaked effects supported for always-on."); 1177 return null; 1178 } 1179 int vibratorId = effects.keyAt(i); 1180 VibratorController vibrator = mVibrators.get(vibratorId); 1181 if (vibrator != null && vibrator.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { 1182 result.put(vibratorId, prebaked); 1183 } 1184 } 1185 if (result.size() == 0) { 1186 return null; 1187 } 1188 return result; 1189 } finally { 1190 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1191 } 1192 } 1193 1194 @Nullable extractPrebakedSegment(VibrationEffect effect)1195 private static PrebakedSegment extractPrebakedSegment(VibrationEffect effect) { 1196 if (effect instanceof VibrationEffect.Composed) { 1197 VibrationEffect.Composed composed = (VibrationEffect.Composed) effect; 1198 if (composed.getSegments().size() == 1) { 1199 VibrationEffectSegment segment = composed.getSegments().get(0); 1200 if (segment instanceof PrebakedSegment) { 1201 return (PrebakedSegment) segment; 1202 } 1203 } 1204 } 1205 return null; 1206 } 1207 1208 /** 1209 * Check given mode, one of the AppOpsManager.MODE_*, against {@link VibrationAttributes} to 1210 * allow bypassing {@link AppOpsManager} checks. 1211 */ 1212 @GuardedBy("mLock") fixupAppOpModeLocked(int mode, VibrationAttributes attrs)1213 private int fixupAppOpModeLocked(int mode, VibrationAttributes attrs) { 1214 if (mode == AppOpsManager.MODE_IGNORED 1215 && attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) { 1216 return AppOpsManager.MODE_ALLOWED; 1217 } 1218 return mode; 1219 } 1220 hasPermission(String permission)1221 private boolean hasPermission(String permission) { 1222 return mContext.checkCallingOrSelfPermission(permission) 1223 == PackageManager.PERMISSION_GRANTED; 1224 } 1225 1226 @GuardedBy("mLock") shouldCancelOnScreenOffLocked(@ullable VibrationStepConductor conductor)1227 private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationStepConductor conductor) { 1228 if (conductor == null) { 1229 return false; 1230 } 1231 HalVibration vib = conductor.getVibration(); 1232 return mVibrationSettings.shouldCancelVibrationOnScreenOff(vib.callerInfo, 1233 vib.stats.getCreateUptimeMillis()); 1234 } 1235 1236 @GuardedBy("mLock") onAllVibratorsLocked(Consumer<VibratorController> consumer)1237 private void onAllVibratorsLocked(Consumer<VibratorController> consumer) { 1238 for (int i = 0; i < mVibrators.size(); i++) { 1239 consumer.accept(mVibrators.valueAt(i)); 1240 } 1241 } 1242 1243 @GuardedBy("mLock") transformAllVibratorsLocked(Function<VibratorController, T> fn)1244 private <T> SparseArray<T> transformAllVibratorsLocked(Function<VibratorController, T> fn) { 1245 SparseArray<T> ret = new SparseArray<>(mVibrators.size()); 1246 for (int i = 0; i < mVibrators.size(); i++) { 1247 ret.put(mVibrators.keyAt(i), fn.apply(mVibrators.valueAt(i))); 1248 } 1249 return ret; 1250 } 1251 1252 /** Point of injection for test dependencies */ 1253 @VisibleForTesting 1254 static class Injector { 1255 getNativeWrapper()1256 NativeWrapper getNativeWrapper() { 1257 return new NativeWrapper(); 1258 } 1259 createHandler(Looper looper)1260 Handler createHandler(Looper looper) { 1261 return new Handler(looper); 1262 } 1263 getBatteryStatsService()1264 IBatteryStats getBatteryStatsService() { 1265 return IBatteryStats.Stub.asInterface(ServiceManager.getService( 1266 BatteryStats.SERVICE_NAME)); 1267 } 1268 getFrameworkStatsLogger(Handler handler)1269 VibratorFrameworkStatsLogger getFrameworkStatsLogger(Handler handler) { 1270 return new VibratorFrameworkStatsLogger(handler); 1271 } 1272 createVibratorController(int vibratorId, VibratorController.OnVibrationCompleteListener listener)1273 VibratorController createVibratorController(int vibratorId, 1274 VibratorController.OnVibrationCompleteListener listener) { 1275 return new VibratorController(vibratorId, listener); 1276 } 1277 addService(String name, IBinder service)1278 void addService(String name, IBinder service) { 1279 ServiceManager.addService(name, service); 1280 } 1281 } 1282 1283 /** 1284 * Implementation of {@link VibrationThread.VibratorManagerHooks} that controls synced 1285 * vibrations and reports them when finished. 1286 */ 1287 private final class VibrationThreadCallbacks implements VibrationThread.VibratorManagerHooks { 1288 1289 @Override prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds)1290 public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) { 1291 if ((mCapabilities & requiredCapabilities) != requiredCapabilities) { 1292 // This sync step requires capabilities this device doesn't have, skipping sync... 1293 return false; 1294 } 1295 return mNativeWrapper.prepareSynced(vibratorIds); 1296 } 1297 1298 @Override triggerSyncedVibration(long vibrationId)1299 public boolean triggerSyncedVibration(long vibrationId) { 1300 return mNativeWrapper.triggerSynced(vibrationId); 1301 } 1302 1303 @Override cancelSyncedVibration()1304 public void cancelSyncedVibration() { 1305 mNativeWrapper.cancelSynced(); 1306 } 1307 1308 @Override noteVibratorOn(int uid, long duration)1309 public void noteVibratorOn(int uid, long duration) { 1310 try { 1311 if (duration <= 0) { 1312 // Tried to turn vibrator ON and got: 1313 // duration == 0: Unsupported effect/method or zero-amplitude segment. 1314 // duration < 0: Unexpected error triggering the vibrator. 1315 // Skip battery stats and atom metric for VibratorStageChanged to ON. 1316 return; 1317 } 1318 if (duration == Long.MAX_VALUE) { 1319 // Repeating duration has started. Report a fixed duration here, noteVibratorOff 1320 // should be called when this is cancelled. 1321 duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION; 1322 } 1323 mBatteryStatsService.noteVibratorOn(uid, duration); 1324 mFrameworkStatsLogger.writeVibratorStateOnAsync(uid, duration); 1325 } catch (RemoteException e) { 1326 Slog.e(TAG, "Error logging VibratorStateChanged to ON", e); 1327 } 1328 } 1329 1330 @Override noteVibratorOff(int uid)1331 public void noteVibratorOff(int uid) { 1332 try { 1333 mBatteryStatsService.noteVibratorOff(uid); 1334 mFrameworkStatsLogger.writeVibratorStateOffAsync(uid); 1335 } catch (RemoteException e) { 1336 Slog.e(TAG, "Error logging VibratorStateChanged to OFF", e); 1337 } 1338 } 1339 1340 @Override onVibrationCompleted(long vibrationId, Vibration.EndInfo vibrationEndInfo)1341 public void onVibrationCompleted(long vibrationId, Vibration.EndInfo vibrationEndInfo) { 1342 if (DEBUG) { 1343 Slog.d(TAG, "Vibration " + vibrationId + " finished with " + vibrationEndInfo); 1344 } 1345 synchronized (mLock) { 1346 if (mCurrentVibration != null 1347 && mCurrentVibration.getVibration().id == vibrationId) { 1348 reportFinishedVibrationLocked(vibrationEndInfo); 1349 } 1350 } 1351 } 1352 1353 @Override onVibrationThreadReleased(long vibrationId)1354 public void onVibrationThreadReleased(long vibrationId) { 1355 if (DEBUG) { 1356 Slog.d(TAG, "VibrationThread released after finished vibration"); 1357 } 1358 synchronized (mLock) { 1359 if (DEBUG) { 1360 Slog.d(TAG, "Processing VibrationThread released callback"); 1361 } 1362 if (Build.IS_DEBUGGABLE && mCurrentVibration != null 1363 && mCurrentVibration.getVibration().id != vibrationId) { 1364 Slog.wtf(TAG, TextUtils.formatSimple( 1365 "VibrationId mismatch on release. expected=%d, released=%d", 1366 mCurrentVibration.getVibration().id, vibrationId)); 1367 } 1368 if (mCurrentVibration != null) { 1369 // This is when we consider the current vibration complete, so report metrics. 1370 mFrameworkStatsLogger.writeVibrationReportedAsync( 1371 mCurrentVibration.getVibration().getStatsInfo( 1372 /* completionUptimeMillis= */ SystemClock.uptimeMillis())); 1373 mCurrentVibration = null; 1374 } 1375 if (mNextVibration != null) { 1376 VibrationStepConductor nextConductor = mNextVibration; 1377 mNextVibration = null; 1378 Vibration.EndInfo vibrationEndInfo = startVibrationOnThreadLocked( 1379 nextConductor); 1380 if (vibrationEndInfo != null) { 1381 // Failed to start the vibration, end it and report metrics right away. 1382 endVibrationLocked(nextConductor.getVibration(), 1383 vibrationEndInfo, /* shouldWriteStats= */ true); 1384 } 1385 } 1386 } 1387 } 1388 } 1389 1390 /** Listener for synced vibration completion callbacks from native. */ 1391 @VisibleForTesting 1392 interface OnSyncedVibrationCompleteListener { 1393 1394 /** Callback triggered when synced vibration is complete. */ onComplete(long vibrationId)1395 void onComplete(long vibrationId); 1396 } 1397 1398 /** 1399 * Implementation of listeners to native vibrators with a weak reference to this service. 1400 */ 1401 private static final class VibrationCompleteListener implements 1402 VibratorController.OnVibrationCompleteListener, OnSyncedVibrationCompleteListener { 1403 private WeakReference<VibratorManagerService> mServiceRef; 1404 VibrationCompleteListener(VibratorManagerService service)1405 VibrationCompleteListener(VibratorManagerService service) { 1406 mServiceRef = new WeakReference<>(service); 1407 } 1408 1409 @Override onComplete(long vibrationId)1410 public void onComplete(long vibrationId) { 1411 VibratorManagerService service = mServiceRef.get(); 1412 if (service != null) { 1413 service.onSyncedVibrationComplete(vibrationId); 1414 } 1415 } 1416 1417 @Override onComplete(int vibratorId, long vibrationId)1418 public void onComplete(int vibratorId, long vibrationId) { 1419 VibratorManagerService service = mServiceRef.get(); 1420 if (service != null) { 1421 service.onVibrationComplete(vibratorId, vibrationId); 1422 } 1423 } 1424 } 1425 1426 /** 1427 * Combination of prekabed vibrations on multiple vibrators, with the same {@link 1428 * VibrationAttributes}, that can be set for always-on effects. 1429 */ 1430 private static final class AlwaysOnVibration { 1431 public final int alwaysOnId; 1432 public final Vibration.CallerInfo callerInfo; 1433 public final SparseArray<PrebakedSegment> effects; 1434 AlwaysOnVibration(int alwaysOnId, Vibration.CallerInfo callerInfo, SparseArray<PrebakedSegment> effects)1435 AlwaysOnVibration(int alwaysOnId, Vibration.CallerInfo callerInfo, 1436 SparseArray<PrebakedSegment> effects) { 1437 this.alwaysOnId = alwaysOnId; 1438 this.callerInfo = callerInfo; 1439 this.effects = effects; 1440 } 1441 } 1442 1443 /** Holder for a {@link ExternalVibration}. */ 1444 private final class ExternalVibrationHolder extends Vibration implements 1445 IBinder.DeathRecipient { 1446 1447 public final ExternalVibration externalVibration; 1448 public int scale; 1449 1450 private Vibration.Status mStatus; 1451 ExternalVibrationHolder(ExternalVibration externalVibration)1452 private ExternalVibrationHolder(ExternalVibration externalVibration) { 1453 super(externalVibration.getToken(), new Vibration.CallerInfo( 1454 externalVibration.getVibrationAttributes(), externalVibration.getUid(), 1455 // TODO(b/243604888): propagating displayID from IExternalVibration instead of 1456 // using INVALID_DISPLAY for all external vibrations. 1457 Display.INVALID_DISPLAY, 1458 externalVibration.getPackage(), null)); 1459 this.externalVibration = externalVibration; 1460 this.scale = IExternalVibratorService.SCALE_NONE; 1461 mStatus = Vibration.Status.RUNNING; 1462 } 1463 mute()1464 public void mute() { 1465 externalVibration.mute(); 1466 } 1467 linkToDeath()1468 public void linkToDeath() { 1469 externalVibration.linkToDeath(this); 1470 } 1471 unlinkToDeath()1472 public void unlinkToDeath() { 1473 externalVibration.unlinkToDeath(this); 1474 } 1475 isHoldingSameVibration(ExternalVibration externalVibration)1476 public boolean isHoldingSameVibration(ExternalVibration externalVibration) { 1477 return this.externalVibration.equals(externalVibration); 1478 } 1479 end(Vibration.EndInfo info)1480 public void end(Vibration.EndInfo info) { 1481 if (mStatus != Vibration.Status.RUNNING) { 1482 // Already ended, ignore this call 1483 return; 1484 } 1485 mStatus = info.status; 1486 stats.reportEnded(info.endedBy); 1487 1488 if (stats.hasStarted()) { 1489 // External vibration doesn't have feedback from total time the vibrator was playing 1490 // with non-zero amplitude, so we use the duration between start and end times of 1491 // the vibration as the time the vibrator was ON, since the haptic channels are 1492 // open for this duration and can receive vibration waveform data. 1493 stats.reportVibratorOn( 1494 stats.getEndUptimeMillis() - stats.getStartUptimeMillis()); 1495 } 1496 } 1497 binderDied()1498 public void binderDied() { 1499 synchronized (mLock) { 1500 if (mCurrentExternalVibration != null) { 1501 if (DEBUG) { 1502 Slog.d(TAG, "External vibration finished because binder died"); 1503 } 1504 endExternalVibrateLocked( 1505 new Vibration.EndInfo(Vibration.Status.CANCELLED_BINDER_DIED), 1506 /* continueExternalControl= */ false); 1507 } 1508 } 1509 } 1510 getDebugInfo()1511 public Vibration.DebugInfo getDebugInfo() { 1512 return new Vibration.DebugInfo(mStatus, stats, /* effect= */ null, 1513 /* originalEffect= */ null, scale, callerInfo); 1514 } 1515 getStatsInfo(long completionUptimeMillis)1516 public VibrationStats.StatsInfo getStatsInfo(long completionUptimeMillis) { 1517 return new VibrationStats.StatsInfo( 1518 externalVibration.getUid(), 1519 FrameworkStatsLog.VIBRATION_REPORTED__VIBRATION_TYPE__EXTERNAL, 1520 externalVibration.getVibrationAttributes().getUsage(), mStatus, stats, 1521 completionUptimeMillis); 1522 } 1523 1524 @Override isRepeating()1525 boolean isRepeating() { 1526 // We don't currently know if the external vibration is repeating, so we just use a 1527 // heuristic based on the usage. Ideally this would be propagated in the 1528 // ExternalVibration. 1529 int usage = externalVibration.getVibrationAttributes().getUsage(); 1530 return usage == VibrationAttributes.USAGE_RINGTONE 1531 || usage == VibrationAttributes.USAGE_ALARM; 1532 } 1533 } 1534 1535 /** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */ 1536 @VisibleForTesting 1537 public static class NativeWrapper { 1538 1539 private long mNativeServicePtr = 0; 1540 1541 /** Returns native pointer to newly created controller and connects with HAL service. */ init(OnSyncedVibrationCompleteListener listener)1542 public void init(OnSyncedVibrationCompleteListener listener) { 1543 mNativeServicePtr = nativeInit(listener); 1544 long finalizerPtr = nativeGetFinalizer(); 1545 1546 if (finalizerPtr != 0) { 1547 NativeAllocationRegistry registry = 1548 NativeAllocationRegistry.createMalloced( 1549 VibratorManagerService.class.getClassLoader(), finalizerPtr); 1550 registry.registerNativeAllocation(this, mNativeServicePtr); 1551 } 1552 } 1553 1554 /** Returns manager capabilities. */ getCapabilities()1555 public long getCapabilities() { 1556 return nativeGetCapabilities(mNativeServicePtr); 1557 } 1558 1559 /** Returns vibrator ids. */ getVibratorIds()1560 public int[] getVibratorIds() { 1561 return nativeGetVibratorIds(mNativeServicePtr); 1562 } 1563 1564 /** Prepare vibrators for triggering vibrations in sync. */ prepareSynced(@onNull int[] vibratorIds)1565 public boolean prepareSynced(@NonNull int[] vibratorIds) { 1566 return nativePrepareSynced(mNativeServicePtr, vibratorIds); 1567 } 1568 1569 /** Trigger prepared synced vibration. */ triggerSynced(long vibrationId)1570 public boolean triggerSynced(long vibrationId) { 1571 return nativeTriggerSynced(mNativeServicePtr, vibrationId); 1572 } 1573 1574 /** Cancel prepared synced vibration. */ cancelSynced()1575 public void cancelSynced() { 1576 nativeCancelSynced(mNativeServicePtr); 1577 } 1578 } 1579 1580 /** Keep records of vibrations played and provide debug information for this service. */ 1581 private static final class VibratorManagerRecords { 1582 private final SparseArray<LinkedList<Vibration.DebugInfo>> mPreviousVibrations = 1583 new SparseArray<>(); 1584 private final LinkedList<Vibration.DebugInfo> mPreviousExternalVibrations = 1585 new LinkedList<>(); 1586 private final int mPreviousVibrationsLimit; 1587 VibratorManagerRecords(int limit)1588 VibratorManagerRecords(int limit) { 1589 mPreviousVibrationsLimit = limit; 1590 } 1591 record(HalVibration vib)1592 synchronized void record(HalVibration vib) { 1593 int usage = vib.callerInfo.attrs.getUsage(); 1594 if (!mPreviousVibrations.contains(usage)) { 1595 mPreviousVibrations.put(usage, new LinkedList<>()); 1596 } 1597 record(mPreviousVibrations.get(usage), vib.getDebugInfo()); 1598 } 1599 record(ExternalVibrationHolder vib)1600 synchronized void record(ExternalVibrationHolder vib) { 1601 record(mPreviousExternalVibrations, vib.getDebugInfo()); 1602 } 1603 record(LinkedList<Vibration.DebugInfo> records, Vibration.DebugInfo info)1604 synchronized void record(LinkedList<Vibration.DebugInfo> records, 1605 Vibration.DebugInfo info) { 1606 if (records.size() > mPreviousVibrationsLimit) { 1607 records.removeFirst(); 1608 } 1609 records.addLast(info); 1610 } 1611 dumpText(PrintWriter pw)1612 synchronized void dumpText(PrintWriter pw) { 1613 for (int i = 0; i < mPreviousVibrations.size(); i++) { 1614 pw.println(); 1615 pw.print(" Previous vibrations for usage "); 1616 pw.print(VibrationAttributes.usageToString(mPreviousVibrations.keyAt(i))); 1617 pw.println(":"); 1618 for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) { 1619 pw.println(" " + info); 1620 } 1621 } 1622 1623 pw.println(); 1624 pw.println(" Previous external vibrations:"); 1625 for (Vibration.DebugInfo info : mPreviousExternalVibrations) { 1626 pw.println(" " + info); 1627 } 1628 } 1629 dumpProto(ProtoOutputStream proto)1630 synchronized void dumpProto(ProtoOutputStream proto) { 1631 for (int i = 0; i < mPreviousVibrations.size(); i++) { 1632 long fieldId; 1633 switch (mPreviousVibrations.keyAt(i)) { 1634 case VibrationAttributes.USAGE_RINGTONE: 1635 fieldId = VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS; 1636 break; 1637 case VibrationAttributes.USAGE_NOTIFICATION: 1638 fieldId = VibratorManagerServiceDumpProto 1639 .PREVIOUS_NOTIFICATION_VIBRATIONS; 1640 break; 1641 case VibrationAttributes.USAGE_ALARM: 1642 fieldId = VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS; 1643 break; 1644 default: 1645 fieldId = VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS; 1646 } 1647 for (Vibration.DebugInfo info : mPreviousVibrations.valueAt(i)) { 1648 info.dumpProto(proto, fieldId); 1649 } 1650 } 1651 1652 for (Vibration.DebugInfo info : mPreviousExternalVibrations) { 1653 info.dumpProto(proto, 1654 VibratorManagerServiceDumpProto.PREVIOUS_EXTERNAL_VIBRATIONS); 1655 } 1656 } 1657 } 1658 1659 /** Clears mNextVibration if set, ending it cleanly */ 1660 @GuardedBy("mLock") clearNextVibrationLocked(Vibration.EndInfo vibrationEndInfo)1661 private void clearNextVibrationLocked(Vibration.EndInfo vibrationEndInfo) { 1662 if (mNextVibration != null) { 1663 if (DEBUG) { 1664 Slog.d(TAG, "Dropping pending vibration " + mNextVibration.getVibration().id 1665 + " with end info: " + vibrationEndInfo); 1666 } 1667 // Clearing next vibration before playing it, end it and report metrics right away. 1668 endVibrationLocked(mNextVibration.getVibration(), vibrationEndInfo, 1669 /* shouldWriteStats= */ true); 1670 mNextVibration = null; 1671 } 1672 } 1673 1674 /** 1675 * Ends the external vibration, and clears related service state. 1676 * 1677 * @param vibrationEndInfo the status and related info to end the associated Vibration with 1678 * @param continueExternalControl indicates whether external control will continue. If not, the 1679 * HAL will have external control turned off. 1680 */ 1681 @GuardedBy("mLock") endExternalVibrateLocked(Vibration.EndInfo vibrationEndInfo, boolean continueExternalControl)1682 private void endExternalVibrateLocked(Vibration.EndInfo vibrationEndInfo, 1683 boolean continueExternalControl) { 1684 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "endExternalVibrateLocked"); 1685 try { 1686 if (mCurrentExternalVibration == null) { 1687 return; 1688 } 1689 mCurrentExternalVibration.unlinkToDeath(); 1690 if (!continueExternalControl) { 1691 setExternalControl(false, mCurrentExternalVibration.stats); 1692 } 1693 // The external control was turned off, end it and report metrics right away. 1694 endVibrationAndWriteStatsLocked(mCurrentExternalVibration, vibrationEndInfo); 1695 mCurrentExternalVibration = null; 1696 } finally { 1697 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1698 } 1699 } 1700 1701 /** Implementation of {@link IExternalVibratorService} to be triggered on external control. */ 1702 @VisibleForTesting 1703 final class ExternalVibratorService extends IExternalVibratorService.Stub { 1704 1705 @Override onExternalVibrationStart(ExternalVibration vib)1706 public int onExternalVibrationStart(ExternalVibration vib) { 1707 if (!hasExternalControlCapability()) { 1708 return IExternalVibratorService.SCALE_MUTE; 1709 } 1710 if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE, 1711 vib.getUid(), -1 /*owningUid*/, true /*exported*/) 1712 != PackageManager.PERMISSION_GRANTED) { 1713 Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid() 1714 + " tried to play externally controlled vibration" 1715 + " without VIBRATE permission, ignoring."); 1716 return IExternalVibratorService.SCALE_MUTE; 1717 } 1718 1719 // Create Vibration.Stats as close to the received request as possible, for tracking. 1720 ExternalVibrationHolder vibHolder = new ExternalVibrationHolder(vib); 1721 VibrationAttributes attrs = fixupVibrationAttributes(vib.getVibrationAttributes(), 1722 /* effect= */ null); 1723 if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) { 1724 // Force update of user settings before checking if this vibration effect should 1725 // be ignored or scaled. 1726 mVibrationSettings.update(); 1727 } 1728 1729 boolean alreadyUnderExternalControl = false; 1730 boolean waitForCompletion = false; 1731 synchronized (mLock) { 1732 // TODO(b/243604888): propagating displayID from IExternalVibration instead of 1733 // using INVALID_DISPLAY for all external vibrations. 1734 Vibration.EndInfo vibrationEndInfo = shouldIgnoreVibrationLocked( 1735 vibHolder.callerInfo); 1736 1737 if (vibrationEndInfo == null 1738 && mCurrentExternalVibration != null 1739 && mCurrentExternalVibration.isHoldingSameVibration(vib)) { 1740 // We are already playing this external vibration, so we can return the same 1741 // scale calculated in the previous call to this method. 1742 return mCurrentExternalVibration.scale; 1743 } 1744 1745 if (vibrationEndInfo == null) { 1746 // Check if ongoing vibration is more important than this vibration. 1747 vibrationEndInfo = shouldIgnoreVibrationForOngoingLocked(vibHolder); 1748 } 1749 1750 if (vibrationEndInfo != null) { 1751 vibHolder.scale = IExternalVibratorService.SCALE_MUTE; 1752 // Failed to start the vibration, end it and report metrics right away. 1753 endVibrationAndWriteStatsLocked(vibHolder, vibrationEndInfo); 1754 return vibHolder.scale; 1755 } 1756 1757 if (mCurrentExternalVibration == null) { 1758 // If we're not under external control right now, then cancel any normal 1759 // vibration that may be playing and ready the vibrator for external control. 1760 if (mCurrentVibration != null) { 1761 vibHolder.stats.reportInterruptedAnotherVibration( 1762 mCurrentVibration.getVibration().callerInfo); 1763 clearNextVibrationLocked( 1764 new Vibration.EndInfo(Vibration.Status.IGNORED_FOR_EXTERNAL, 1765 vibHolder.callerInfo)); 1766 mCurrentVibration.notifyCancelled( 1767 new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED, 1768 vibHolder.callerInfo), 1769 /* immediate= */ true); 1770 waitForCompletion = true; 1771 } 1772 } else { 1773 // At this point we have an externally controlled vibration playing already. 1774 // Since the interface defines that only one externally controlled vibration can 1775 // play at a time, we need to first mute the ongoing vibration and then return 1776 // a scale from this function for the new one, so we can be assured that the 1777 // ongoing will be muted in favor of the new vibration. 1778 // 1779 // Note that this doesn't support multiple concurrent external controls, as we 1780 // would need to mute the old one still if it came from a different controller. 1781 alreadyUnderExternalControl = true; 1782 mCurrentExternalVibration.mute(); 1783 vibHolder.stats.reportInterruptedAnotherVibration( 1784 mCurrentExternalVibration.callerInfo); 1785 endExternalVibrateLocked( 1786 new Vibration.EndInfo(Vibration.Status.CANCELLED_SUPERSEDED, 1787 vibHolder.callerInfo), 1788 /* continueExternalControl= */ true); 1789 } 1790 mCurrentExternalVibration = vibHolder; 1791 vibHolder.linkToDeath(); 1792 vibHolder.scale = mVibrationScaler.getExternalVibrationScale(attrs.getUsage()); 1793 } 1794 1795 if (waitForCompletion) { 1796 if (!mVibrationThread.waitForThreadIdle(VIBRATION_CANCEL_WAIT_MILLIS)) { 1797 Slog.e(TAG, "Timed out waiting for vibration to cancel"); 1798 synchronized (mLock) { 1799 // Trigger endExternalVibrateLocked to unlink to death recipient. 1800 endExternalVibrateLocked( 1801 new Vibration.EndInfo(Vibration.Status.IGNORED_ERROR_CANCELLING), 1802 /* continueExternalControl= */ false); 1803 } 1804 return IExternalVibratorService.SCALE_MUTE; 1805 } 1806 } 1807 if (!alreadyUnderExternalControl) { 1808 if (DEBUG) { 1809 Slog.d(TAG, "Vibrator going under external control."); 1810 } 1811 setExternalControl(true, vibHolder.stats); 1812 } 1813 if (DEBUG) { 1814 Slog.d(TAG, "Playing external vibration: " + vib); 1815 } 1816 // Vibrator will start receiving data from external channels after this point. 1817 // Report current time as the vibration start time, for debugging. 1818 vibHolder.stats.reportStarted(); 1819 return vibHolder.scale; 1820 } 1821 1822 @Override onExternalVibrationStop(ExternalVibration vib)1823 public void onExternalVibrationStop(ExternalVibration vib) { 1824 synchronized (mLock) { 1825 if (mCurrentExternalVibration != null 1826 && mCurrentExternalVibration.isHoldingSameVibration(vib)) { 1827 if (DEBUG) { 1828 Slog.d(TAG, "Stopping external vibration: " + vib); 1829 } 1830 endExternalVibrateLocked( 1831 new Vibration.EndInfo(Vibration.Status.FINISHED), 1832 /* continueExternalControl= */ false); 1833 } 1834 } 1835 } 1836 hasExternalControlCapability()1837 private boolean hasExternalControlCapability() { 1838 for (int i = 0; i < mVibrators.size(); i++) { 1839 if (mVibrators.valueAt(i).hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { 1840 return true; 1841 } 1842 } 1843 return false; 1844 } 1845 } 1846 1847 /** Provide limited functionality from {@link VibratorManagerService} as shell commands. */ 1848 private final class VibratorManagerShellCommand extends ShellCommand { 1849 public static final String SHELL_PACKAGE_NAME = "com.android.shell"; 1850 1851 private final class CommonOptions { 1852 public boolean force = false; 1853 public String description = "Shell command"; 1854 public boolean background = false; 1855 CommonOptions()1856 CommonOptions() { 1857 String nextArg; 1858 while ((nextArg = peekNextArg()) != null) { 1859 switch (nextArg) { 1860 case "-f": 1861 getNextArgRequired(); // consume "-f" 1862 force = true; 1863 break; 1864 case "-B": 1865 getNextArgRequired(); // consume "-B" 1866 background = true; 1867 break; 1868 case "-d": 1869 getNextArgRequired(); // consume "-d" 1870 description = getNextArgRequired(); 1871 break; 1872 default: 1873 // nextArg is not a common option, finish reading. 1874 return; 1875 } 1876 } 1877 } 1878 } 1879 1880 private final IBinder mShellCallbacksToken; 1881 VibratorManagerShellCommand(IBinder shellCallbacksToken)1882 private VibratorManagerShellCommand(IBinder shellCallbacksToken) { 1883 mShellCallbacksToken = shellCallbacksToken; 1884 } 1885 1886 @Override onCommand(String cmd)1887 public int onCommand(String cmd) { 1888 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "onCommand " + cmd); 1889 try { 1890 if ("list".equals(cmd)) { 1891 return runListVibrators(); 1892 } 1893 if ("synced".equals(cmd)) { 1894 return runMono(); 1895 } 1896 if ("combined".equals(cmd)) { 1897 return runStereo(); 1898 } 1899 if ("sequential".equals(cmd)) { 1900 return runSequential(); 1901 } 1902 if ("cancel".equals(cmd)) { 1903 return runCancel(); 1904 } 1905 return handleDefaultCommands(cmd); 1906 } finally { 1907 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1908 } 1909 } 1910 runListVibrators()1911 private int runListVibrators() { 1912 try (PrintWriter pw = getOutPrintWriter();) { 1913 if (mVibratorIds.length == 0) { 1914 pw.println("No vibrator found"); 1915 } else { 1916 for (int id : mVibratorIds) { 1917 pw.println(id); 1918 } 1919 } 1920 pw.println(""); 1921 return 0; 1922 } 1923 } 1924 1925 /** 1926 * Runs a CombinedVibration using the configured common options and attributes. 1927 */ runVibrate(CommonOptions commonOptions, CombinedVibration combined)1928 private void runVibrate(CommonOptions commonOptions, CombinedVibration combined) { 1929 VibrationAttributes attrs = createVibrationAttributes(commonOptions); 1930 // If running in the background, bind to death of the server binder rather than the 1931 // client, and the cancel command likewise uses the server binder reference to 1932 // only cancel background vibrations. 1933 IBinder deathBinder = commonOptions.background ? VibratorManagerService.this 1934 : mShellCallbacksToken; 1935 HalVibration vib = vibrateInternal(Binder.getCallingUid(), Display.DEFAULT_DISPLAY, 1936 SHELL_PACKAGE_NAME, combined, attrs, commonOptions.description, deathBinder); 1937 if (vib != null && !commonOptions.background) { 1938 try { 1939 // Waits for the client vibration to finish, but the VibrationThread may still 1940 // do cleanup after this. 1941 vib.waitForEnd(); 1942 } catch (InterruptedException e) { 1943 } 1944 } 1945 } 1946 runMono()1947 private int runMono() { 1948 runVibrate(new CommonOptions(), CombinedVibration.createParallel(nextEffect())); 1949 return 0; 1950 } 1951 runStereo()1952 private int runStereo() { 1953 CommonOptions commonOptions = new CommonOptions(); 1954 CombinedVibration.ParallelCombination combination = 1955 CombinedVibration.startParallel(); 1956 while ("-v".equals(getNextOption())) { 1957 int vibratorId = Integer.parseInt(getNextArgRequired()); 1958 combination.addVibrator(vibratorId, nextEffect()); 1959 } 1960 runVibrate(commonOptions, combination.combine()); 1961 return 0; 1962 } 1963 runSequential()1964 private int runSequential() { 1965 CommonOptions commonOptions = new CommonOptions(); 1966 CombinedVibration.SequentialCombination combination = 1967 CombinedVibration.startSequential(); 1968 while ("-v".equals(getNextOption())) { 1969 int vibratorId = Integer.parseInt(getNextArgRequired()); 1970 combination.addNext(vibratorId, nextEffect()); 1971 } 1972 runVibrate(commonOptions, combination.combine()); 1973 return 0; 1974 } 1975 runCancel()1976 private int runCancel() { 1977 // Cancel is only needed if the vibration was run in the background, otherwise it's 1978 // terminated by the shell command ending. In these cases, the token was that of the 1979 // service rather than the client. 1980 cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, VibratorManagerService.this); 1981 return 0; 1982 } 1983 nextEffect()1984 private VibrationEffect nextEffect() { 1985 VibrationEffect.Composition composition = VibrationEffect.startComposition(); 1986 String nextArg; 1987 1988 while ((nextArg = peekNextArg()) != null) { 1989 if ("oneshot".equals(nextArg)) { 1990 addOneShotToComposition(composition); 1991 } else if ("waveform".equals(nextArg)) { 1992 addWaveformToComposition(composition); 1993 } else if ("prebaked".equals(nextArg)) { 1994 addPrebakedToComposition(composition); 1995 } else if ("primitives".equals(nextArg)) { 1996 addPrimitivesToComposition(composition); 1997 } else { 1998 // nextArg is not an effect, finish reading. 1999 break; 2000 } 2001 } 2002 2003 return composition.compose(); 2004 } 2005 addOneShotToComposition(VibrationEffect.Composition composition)2006 private void addOneShotToComposition(VibrationEffect.Composition composition) { 2007 boolean hasAmplitude = false; 2008 int delay = 0; 2009 2010 getNextArgRequired(); // consume "oneshot" 2011 String nextOption; 2012 while ((nextOption = getNextOption()) != null) { 2013 if ("-a".equals(nextOption)) { 2014 hasAmplitude = true; 2015 } else if ("-w".equals(nextOption)) { 2016 delay = Integer.parseInt(getNextArgRequired()); 2017 } 2018 } 2019 2020 long duration = Long.parseLong(getNextArgRequired()); 2021 int amplitude = hasAmplitude ? Integer.parseInt(getNextArgRequired()) 2022 : VibrationEffect.DEFAULT_AMPLITUDE; 2023 composition.addOffDuration(Duration.ofMillis(delay)); 2024 composition.addEffect(VibrationEffect.createOneShot(duration, amplitude)); 2025 } 2026 addWaveformToComposition(VibrationEffect.Composition composition)2027 private void addWaveformToComposition(VibrationEffect.Composition composition) { 2028 boolean hasAmplitudes = false; 2029 boolean hasFrequencies = false; 2030 boolean isContinuous = false; 2031 int repeat = -1; 2032 int delay = 0; 2033 2034 getNextArgRequired(); // consume "waveform" 2035 String nextOption; 2036 while ((nextOption = getNextOption()) != null) { 2037 if ("-a".equals(nextOption)) { 2038 hasAmplitudes = true; 2039 } else if ("-r".equals(nextOption)) { 2040 repeat = Integer.parseInt(getNextArgRequired()); 2041 } else if ("-w".equals(nextOption)) { 2042 delay = Integer.parseInt(getNextArgRequired()); 2043 } else if ("-f".equals(nextOption)) { 2044 hasFrequencies = true; 2045 } else if ("-c".equals(nextOption)) { 2046 isContinuous = true; 2047 } 2048 } 2049 List<Integer> durations = new ArrayList<>(); 2050 List<Float> amplitudes = new ArrayList<>(); 2051 List<Float> frequencies = new ArrayList<>(); 2052 2053 float nextAmplitude = 0; 2054 String nextArg; 2055 while ((nextArg = peekNextArg()) != null) { 2056 try { 2057 durations.add(Integer.parseInt(nextArg)); 2058 getNextArgRequired(); // consume the duration 2059 } catch (NumberFormatException e) { 2060 // nextArg is not a duration, finish reading. 2061 break; 2062 } 2063 if (hasAmplitudes) { 2064 amplitudes.add( 2065 Float.parseFloat(getNextArgRequired()) / VibrationEffect.MAX_AMPLITUDE); 2066 } else { 2067 amplitudes.add(nextAmplitude); 2068 nextAmplitude = 1 - nextAmplitude; 2069 } 2070 if (hasFrequencies) { 2071 frequencies.add(Float.parseFloat(getNextArgRequired())); 2072 } 2073 } 2074 2075 // Add delay before the waveform. 2076 composition.addOffDuration(Duration.ofMillis(delay)); 2077 2078 VibrationEffect.WaveformBuilder waveform = VibrationEffect.startWaveform(); 2079 for (int i = 0; i < durations.size(); i++) { 2080 Duration transitionDuration = isContinuous 2081 ? Duration.ofMillis(durations.get(i)) 2082 : Duration.ZERO; 2083 Duration sustainDuration = isContinuous 2084 ? Duration.ZERO 2085 : Duration.ofMillis(durations.get(i)); 2086 2087 if (hasFrequencies) { 2088 waveform.addTransition(transitionDuration, targetAmplitude(amplitudes.get(i)), 2089 targetFrequency(frequencies.get(i))); 2090 } else { 2091 waveform.addTransition(transitionDuration, targetAmplitude(amplitudes.get(i))); 2092 } 2093 if (!sustainDuration.isZero()) { 2094 // Add sustain only takes positive durations. Skip this since we already 2095 // did a transition to the desired values (even when duration is zero). 2096 waveform.addSustain(sustainDuration); 2097 } 2098 2099 if ((i > 0) && (i == repeat)) { 2100 // Add segment that is not repeated to the composition and reset builder. 2101 composition.addEffect(waveform.build()); 2102 2103 if (hasFrequencies) { 2104 waveform = VibrationEffect.startWaveform(targetAmplitude(amplitudes.get(i)), 2105 targetFrequency(frequencies.get(i))); 2106 } else { 2107 waveform = VibrationEffect.startWaveform( 2108 targetAmplitude(amplitudes.get(i))); 2109 } 2110 } 2111 } 2112 if (repeat < 0) { 2113 composition.addEffect(waveform.build()); 2114 } else { 2115 // The waveform was already split at the repeat index, just repeat what remains. 2116 composition.repeatEffectIndefinitely(waveform.build()); 2117 } 2118 } 2119 addPrebakedToComposition(VibrationEffect.Composition composition)2120 private void addPrebakedToComposition(VibrationEffect.Composition composition) { 2121 boolean shouldFallback = false; 2122 int delay = 0; 2123 2124 getNextArgRequired(); // consume "prebaked" 2125 String nextOption; 2126 while ((nextOption = getNextOption()) != null) { 2127 if ("-b".equals(nextOption)) { 2128 shouldFallback = true; 2129 } else if ("-w".equals(nextOption)) { 2130 delay = Integer.parseInt(getNextArgRequired()); 2131 } 2132 } 2133 2134 int effectId = Integer.parseInt(getNextArgRequired()); 2135 composition.addOffDuration(Duration.ofMillis(delay)); 2136 composition.addEffect(VibrationEffect.get(effectId, shouldFallback)); 2137 } 2138 addPrimitivesToComposition(VibrationEffect.Composition composition)2139 private void addPrimitivesToComposition(VibrationEffect.Composition composition) { 2140 getNextArgRequired(); // consume "primitives" 2141 String nextArg; 2142 while ((nextArg = peekNextArg()) != null) { 2143 int delay = 0; 2144 if ("-w".equals(nextArg)) { 2145 getNextArgRequired(); // consume "-w" 2146 delay = Integer.parseInt(getNextArgRequired()); 2147 nextArg = peekNextArg(); 2148 } 2149 try { 2150 composition.addPrimitive(Integer.parseInt(nextArg), /* scale= */ 1, delay); 2151 getNextArgRequired(); // consume the primitive id 2152 } catch (NumberFormatException | NullPointerException e) { 2153 // nextArg is not describing a primitive, leave it to be consumed by outer loops 2154 break; 2155 } 2156 } 2157 } 2158 createVibrationAttributes(CommonOptions commonOptions)2159 private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) { 2160 final int flags = 2161 commonOptions.force ? VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY : 0; 2162 return new VibrationAttributes.Builder() 2163 .setFlags(flags) 2164 // Used to apply Settings.System.HAPTIC_FEEDBACK_INTENSITY to scale effects. 2165 .setUsage(VibrationAttributes.USAGE_TOUCH) 2166 .build(); 2167 } 2168 2169 @Override onHelp()2170 public void onHelp() { 2171 try (PrintWriter pw = getOutPrintWriter();) { 2172 pw.println("Vibrator Manager commands:"); 2173 pw.println(" help"); 2174 pw.println(" Prints this help text."); 2175 pw.println(""); 2176 pw.println(" list"); 2177 pw.println(" Prints the id of device vibrators. This does not include any "); 2178 pw.println(" connected input device."); 2179 pw.println(" synced [options] <effect>..."); 2180 pw.println(" Vibrates effect on all vibrators in sync."); 2181 pw.println(" combined [options] (-v <vibrator-id> <effect>...)..."); 2182 pw.println(" Vibrates different effects on each vibrator in sync."); 2183 pw.println(" sequential [options] (-v <vibrator-id> <effect>...)..."); 2184 pw.println(" Vibrates different effects on each vibrator in sequence."); 2185 pw.println(" cancel"); 2186 pw.println(" Cancels any active vibration"); 2187 pw.println(""); 2188 pw.println("Effect commands:"); 2189 pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]"); 2190 pw.println(" Vibrates for duration milliseconds; ignored when device is on "); 2191 pw.println(" DND (Do Not Disturb) mode; touch feedback strength user setting "); 2192 pw.println(" will be used to scale amplitude."); 2193 pw.println(" If -w is provided, the effect will be played after the specified"); 2194 pw.println(" wait time in milliseconds."); 2195 pw.println(" If -a is provided, the command accepts a second argument for "); 2196 pw.println(" amplitude, in a scale of 1-255."); 2197 pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] "); 2198 pw.println("(<duration> [<amplitude>] [<frequency>])..."); 2199 pw.println(" Vibrates for durations and amplitudes in list; ignored when "); 2200 pw.println(" device is on DND (Do Not Disturb) mode; touch feedback strength "); 2201 pw.println(" user setting will be used to scale amplitude."); 2202 pw.println(" If -w is provided, the effect will be played after the specified"); 2203 pw.println(" wait time in milliseconds."); 2204 pw.println(" If -r is provided, the waveform loops back to the specified"); 2205 pw.println(" index (e.g. 0 loops from the beginning)"); 2206 pw.println(" If -a is provided, the command expects amplitude to follow each"); 2207 pw.println(" duration; otherwise, it accepts durations only and alternates"); 2208 pw.println(" off/on"); 2209 pw.println(" If -f is provided, the command expects frequency to follow each"); 2210 pw.println(" amplitude or duration; otherwise, it uses resonant frequency"); 2211 pw.println(" If -c is provided, the waveform is continuous and will ramp"); 2212 pw.println(" between values; otherwise each entry is a fixed step."); 2213 pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;"); 2214 pw.println(" frequency is an absolute value in hertz;"); 2215 pw.println(" prebaked [-w delay] [-b] <effect-id>"); 2216 pw.println(" Vibrates with prebaked effect; ignored when device is on DND "); 2217 pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); 2218 pw.println(" will be used to scale amplitude."); 2219 pw.println(" If -w is provided, the effect will be played after the specified"); 2220 pw.println(" wait time in milliseconds."); 2221 pw.println(" If -b is provided, the prebaked fallback effect will be played if"); 2222 pw.println(" the device doesn't support the given effect-id."); 2223 pw.println(" primitives ([-w delay] <primitive-id>)..."); 2224 pw.println(" Vibrates with a composed effect; ignored when device is on DND "); 2225 pw.println(" (Do Not Disturb) mode; touch feedback strength user setting "); 2226 pw.println(" will be used to scale primitive intensities."); 2227 pw.println(" If -w is provided, the next primitive will be played after the "); 2228 pw.println(" specified wait time in milliseconds."); 2229 pw.println(""); 2230 pw.println("Common Options:"); 2231 pw.println(" -f"); 2232 pw.println(" Force. Ignore Do Not Disturb setting."); 2233 pw.println(" -B"); 2234 pw.println(" Run in the background; without this option the shell cmd will"); 2235 pw.println(" block until the vibration has completed."); 2236 pw.println(" -d <description>"); 2237 pw.println(" Add description to the vibration."); 2238 pw.println(""); 2239 } 2240 } 2241 } 2242 } 2243