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.Trace.TRACE_TAG_VIBRATOR; 20 import static android.os.VibrationAttributes.USAGE_CLASS_ALARM; 21 import static android.os.VibrationEffect.VibrationParameter.targetAmplitude; 22 import static android.os.VibrationEffect.VibrationParameter.targetFrequency; 23 24 import android.annotation.NonNull; 25 import android.annotation.Nullable; 26 import android.app.ActivityManager; 27 import android.app.AppOpsManager; 28 import android.content.BroadcastReceiver; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.content.IntentFilter; 32 import android.content.pm.PackageManager; 33 import android.content.res.Resources; 34 import android.hardware.vibrator.IVibrator; 35 import android.hardware.vibrator.IVibratorManager; 36 import android.os.BatteryStats; 37 import android.os.Binder; 38 import android.os.Build; 39 import android.os.CombinedVibration; 40 import android.os.ExternalVibration; 41 import android.os.ExternalVibrationScale; 42 import android.os.Handler; 43 import android.os.IBinder; 44 import android.os.ICancellationSignal; 45 import android.os.IExternalVibratorService; 46 import android.os.IVibratorManagerService; 47 import android.os.IVibratorStateListener; 48 import android.os.Looper; 49 import android.os.PowerManager; 50 import android.os.Process; 51 import android.os.RemoteException; 52 import android.os.ResultReceiver; 53 import android.os.ServiceManager; 54 import android.os.ShellCallback; 55 import android.os.ShellCommand; 56 import android.os.SystemClock; 57 import android.os.Trace; 58 import android.os.VibrationAttributes; 59 import android.os.VibrationEffect; 60 import android.os.VibratorInfo; 61 import android.os.vibrator.Flags; 62 import android.os.vibrator.IVibrationSessionCallback; 63 import android.os.vibrator.PrebakedSegment; 64 import android.os.vibrator.PrimitiveSegment; 65 import android.os.vibrator.VibrationConfig; 66 import android.os.vibrator.VibrationEffectSegment; 67 import android.os.vibrator.VibratorInfoFactory; 68 import android.os.vibrator.persistence.ParsedVibration; 69 import android.os.vibrator.persistence.VibrationXmlParser; 70 import android.text.TextUtils; 71 import android.util.IndentingPrintWriter; 72 import android.util.Slog; 73 import android.util.SparseArray; 74 import android.util.proto.ProtoOutputStream; 75 import android.view.HapticFeedbackConstants; 76 77 import com.android.internal.annotations.GuardedBy; 78 import com.android.internal.annotations.VisibleForTesting; 79 import com.android.internal.app.IBatteryStats; 80 import com.android.internal.util.DumpUtils; 81 import com.android.server.SystemService; 82 import com.android.server.pm.BackgroundUserSoundNotifier; 83 import com.android.server.pm.UserManagerInternal; 84 import com.android.server.vibrator.VibrationSession.CallerInfo; 85 import com.android.server.vibrator.VibrationSession.DebugInfo; 86 import com.android.server.vibrator.VibrationSession.Status; 87 88 import libcore.util.NativeAllocationRegistry; 89 90 import java.io.FileDescriptor; 91 import java.io.IOException; 92 import java.io.PrintWriter; 93 import java.io.StringReader; 94 import java.lang.ref.WeakReference; 95 import java.time.Duration; 96 import java.util.ArrayList; 97 import java.util.Arrays; 98 import java.util.List; 99 import java.util.Objects; 100 import java.util.concurrent.CompletableFuture; 101 import java.util.function.Consumer; 102 import java.util.function.Function; 103 import java.util.function.Predicate; 104 105 /** System implementation of {@link IVibratorManagerService}. */ 106 public class VibratorManagerService extends IVibratorManagerService.Stub { 107 private static final String TAG = "VibratorManagerService"; 108 private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service"; 109 private static final String VIBRATOR_CONTROL_SERVICE = 110 "android.frameworks.vibrator.IVibratorControlService/default"; 111 // To enable these logs, run: 112 // 'adb shell setprop persist.log.tag.VibratorManagerService DEBUG && adb reboot' 113 private static final boolean DEBUG = VibratorDebugUtils.isDebuggable(TAG); 114 private static final VibrationAttributes DEFAULT_ATTRIBUTES = 115 new VibrationAttributes.Builder().build(); 116 private static final int ATTRIBUTES_ALL_BYPASS_FLAGS = 117 VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY 118 | VibrationAttributes.FLAG_BYPASS_USER_VIBRATION_INTENSITY_OFF; 119 120 /** Fixed large duration used to note repeating vibrations to {@link IBatteryStats}. */ 121 private static final long BATTERY_STATS_REPEATING_VIBRATION_DURATION = 5_000; 122 123 /** 124 * Maximum millis to wait for a vibration thread cancellation to "clean up" and finish, when 125 * blocking for an external vibration. In practice, this should be plenty. 126 */ 127 private static final long VIBRATION_CANCEL_WAIT_MILLIS = 5000; 128 129 /** Lifecycle responsible for initializing this class at the right system server phases. */ 130 public static class Lifecycle extends SystemService { 131 private VibratorManagerService mService; 132 Lifecycle(Context context)133 public Lifecycle(Context context) { 134 super(context); 135 } 136 137 @Override onStart()138 public void onStart() { 139 mService = new VibratorManagerService(getContext(), new Injector()); 140 publishBinderService(Context.VIBRATOR_MANAGER_SERVICE, mService); 141 } 142 143 @Override onBootPhase(int phase)144 public void onBootPhase(int phase) { 145 if (phase == SystemService.PHASE_SYSTEM_SERVICES_READY) { 146 mService.systemReady(); 147 } 148 } 149 } 150 151 private final Object mLock = new Object(); 152 private final Context mContext; 153 private final Injector mInjector; 154 private final PowerManager.WakeLock mWakeLock; 155 private final IBatteryStats mBatteryStatsService; 156 private final VibratorFrameworkStatsLogger mFrameworkStatsLogger; 157 private final Handler mHandler; 158 private final VibrationThread mVibrationThread; 159 private final AppOpsManager mAppOps; 160 private final NativeWrapper mNativeWrapper; 161 private final VibratorManagerRecords mVibratorManagerRecords; 162 private final long mCapabilities; 163 private final int[] mVibratorIds; 164 private final SparseArray<VibratorController> mVibrators; 165 private final VibrationThreadCallbacks mVibrationThreadCallbacks = 166 new VibrationThreadCallbacks(); 167 private final ExternalVibrationCallbacks mExternalVibrationCallbacks = 168 new ExternalVibrationCallbacks(); 169 private final VendorVibrationSessionCallbacks mVendorVibrationSessionCallbacks = 170 new VendorVibrationSessionCallbacks(); 171 @GuardedBy("mLock") 172 private final SparseArray<AlwaysOnVibration> mAlwaysOnEffects = new SparseArray<>(); 173 @GuardedBy("mLock") 174 private VibrationSession mCurrentSession; 175 @GuardedBy("mLock") 176 private VibrationSession mNextSession; 177 @GuardedBy("mLock") 178 private boolean mServiceReady; 179 180 @VisibleForTesting 181 final VibrationSettings mVibrationSettings; 182 private final VibrationConfig mVibrationConfig; 183 private final VibrationScaler mVibrationScaler; 184 private final VibratorControlService mVibratorControlService; 185 private final InputDeviceDelegate mInputDeviceDelegate; 186 private final DeviceAdapter mDeviceAdapter; 187 188 @GuardedBy("mLock") 189 @Nullable private SparseArray<VibratorInfo> mVibratorInfos; 190 @GuardedBy("mLock") 191 @Nullable private VibratorInfo mCombinedVibratorInfo; 192 @GuardedBy("mLock") 193 @Nullable private HapticFeedbackVibrationProvider mHapticFeedbackVibrationProvider; 194 195 @VisibleForTesting 196 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 197 @Override 198 public void onReceive(Context context, Intent intent) { 199 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 200 // When the system is entering a non-interactive state, we want to cancel 201 // vibrations in case a misbehaving app has abandoned them. 202 synchronized (mLock) { 203 maybeClearCurrentAndNextSessionsLocked( 204 VibratorManagerService.this::shouldCancelOnScreenOffLocked, 205 Status.CANCELLED_BY_SCREEN_OFF); 206 } 207 } else if (UserManagerInternal.shouldShowNotificationForBackgroundUserSounds() 208 && intent.getAction().equals(BackgroundUserSoundNotifier.ACTION_MUTE_SOUND)) { 209 synchronized (mLock) { 210 maybeClearCurrentAndNextSessionsLocked( 211 VibratorManagerService.this::shouldCancelOnFgUserRequest, 212 Status.CANCELLED_BY_FOREGROUND_USER); 213 } 214 } 215 } 216 }; 217 218 @VisibleForTesting 219 final AppOpsManager.OnOpChangedInternalListener mAppOpsChangeListener = 220 new AppOpsManager.OnOpChangedInternalListener() { 221 @Override 222 public void onOpChanged(int op, String packageName) { 223 if (op != AppOpsManager.OP_VIBRATE) { 224 return; 225 } 226 synchronized (mLock) { 227 maybeClearCurrentAndNextSessionsLocked( 228 VibratorManagerService.this::shouldCancelAppOpModeChangedLocked, 229 Status.CANCELLED_BY_APP_OPS); 230 } 231 } 232 }; 233 nativeInit(VibratorManagerNativeCallbacks listener)234 static native long nativeInit(VibratorManagerNativeCallbacks listener); 235 nativeGetFinalizer()236 static native long nativeGetFinalizer(); 237 nativeGetCapabilities(long nativeServicePtr)238 static native long nativeGetCapabilities(long nativeServicePtr); 239 nativeGetVibratorIds(long nativeServicePtr)240 static native int[] nativeGetVibratorIds(long nativeServicePtr); 241 nativePrepareSynced(long nativeServicePtr, int[] vibratorIds)242 static native boolean nativePrepareSynced(long nativeServicePtr, int[] vibratorIds); 243 nativeTriggerSynced(long nativeServicePtr, long vibrationId)244 static native boolean nativeTriggerSynced(long nativeServicePtr, long vibrationId); 245 nativeCancelSynced(long nativeServicePtr)246 static native void nativeCancelSynced(long nativeServicePtr); 247 nativeStartSession(long nativeServicePtr, long sessionId, int[] vibratorIds)248 static native boolean nativeStartSession(long nativeServicePtr, long sessionId, 249 int[] vibratorIds); 250 nativeEndSession(long nativeServicePtr, long sessionId, boolean shouldAbort)251 static native void nativeEndSession(long nativeServicePtr, long sessionId, boolean shouldAbort); 252 nativeClearSessions(long nativeServicePtr)253 static native void nativeClearSessions(long nativeServicePtr); 254 255 @VisibleForTesting VibratorManagerService(Context context, Injector injector)256 VibratorManagerService(Context context, Injector injector) { 257 mContext = context; 258 mInjector = injector; 259 mHandler = injector.createHandler(Looper.myLooper()); 260 mFrameworkStatsLogger = injector.getFrameworkStatsLogger(mHandler); 261 262 mVibrationConfig = new VibrationConfig(context.getResources()); 263 mVibrationSettings = new VibrationSettings(mContext, mHandler, mVibrationConfig); 264 mVibrationScaler = new VibrationScaler(mVibrationConfig, mVibrationSettings); 265 mVibratorControlService = new VibratorControlService(mContext, 266 injector.createVibratorControllerHolder(), mVibrationScaler, mVibrationSettings, 267 mFrameworkStatsLogger, mLock); 268 mInputDeviceDelegate = new InputDeviceDelegate(mContext, mHandler); 269 270 VibrationCompleteListener listener = new VibrationCompleteListener(this); 271 mNativeWrapper = injector.getNativeWrapper(); 272 mNativeWrapper.init(listener); 273 274 int recentDumpSizeLimit = mContext.getResources().getInteger( 275 com.android.internal.R.integer.config_recentVibrationsDumpSizeLimit); 276 int dumpSizeLimit = mContext.getResources().getInteger( 277 com.android.internal.R.integer.config_previousVibrationsDumpSizeLimit); 278 int dumpAggregationTimeLimit = mContext.getResources().getInteger( 279 com.android.internal.R.integer 280 .config_previousVibrationsDumpAggregationTimeMillisLimit); 281 mVibratorManagerRecords = new VibratorManagerRecords( 282 recentDumpSizeLimit, dumpSizeLimit, dumpAggregationTimeLimit); 283 284 mBatteryStatsService = injector.getBatteryStatsService(); 285 286 mAppOps = mContext.getSystemService(AppOpsManager.class); 287 if (Flags.cancelByAppops()) { 288 mAppOps.startWatchingMode(AppOpsManager.OP_VIBRATE, null, mAppOpsChangeListener); 289 } 290 291 PowerManager pm = context.getSystemService(PowerManager.class); 292 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); 293 mWakeLock.setReferenceCounted(true); 294 mVibrationThread = new VibrationThread(mWakeLock, mVibrationThreadCallbacks); 295 mVibrationThread.start(); 296 297 // Load vibrator hardware info. The vibrator ids and manager capabilities are loaded only 298 // once and assumed unchanged for the lifecycle of this service. Each individual vibrator 299 // can still retry loading each individual vibrator hardware spec once more at systemReady. 300 mCapabilities = mNativeWrapper.getCapabilities(); 301 int[] vibratorIds = mNativeWrapper.getVibratorIds(); 302 if (vibratorIds == null) { 303 mVibratorIds = new int[0]; 304 mVibrators = new SparseArray<>(0); 305 } else { 306 // Keep original vibrator id order, which might be meaningful. 307 mVibratorIds = vibratorIds; 308 mVibrators = new SparseArray<>(mVibratorIds.length); 309 for (int vibratorId : vibratorIds) { 310 VibratorController vibratorController = 311 injector.createVibratorController(vibratorId, listener); 312 mVibrators.put(vibratorId, vibratorController); 313 } 314 } 315 316 // Load vibrator adapter, that depends on hardware info. 317 mDeviceAdapter = new DeviceAdapter(mVibrationSettings, mVibrators); 318 319 // Reset the hardware to a default state, in case this is a runtime restart instead of a 320 // fresh boot. 321 mNativeWrapper.cancelSynced(); 322 if (Flags.vendorVibrationEffects()) { 323 mNativeWrapper.clearSessions(); 324 } 325 for (int i = 0; i < mVibrators.size(); i++) { 326 mVibrators.valueAt(i).reset(); 327 } 328 329 IntentFilter filter = new IntentFilter(); 330 filter.addAction(Intent.ACTION_SCREEN_OFF); 331 if (UserManagerInternal.shouldShowNotificationForBackgroundUserSounds()) { 332 filter.addAction(BackgroundUserSoundNotifier.ACTION_MUTE_SOUND); 333 } 334 context.registerReceiver(mIntentReceiver, filter, Context.RECEIVER_NOT_EXPORTED); 335 336 injector.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService()); 337 if (injector.isServiceDeclared(VIBRATOR_CONTROL_SERVICE)) { 338 injector.addService(VIBRATOR_CONTROL_SERVICE, mVibratorControlService); 339 } 340 341 } 342 343 /** Finish initialization at boot phase {@link SystemService#PHASE_SYSTEM_SERVICES_READY}. */ 344 @VisibleForTesting systemReady()345 void systemReady() { 346 Slog.v(TAG, "Initializing VibratorManager service..."); 347 Trace.traceBegin(TRACE_TAG_VIBRATOR, "systemReady"); 348 try { 349 // Will retry to load each vibrator's info, if any request have failed. 350 for (int i = 0; i < mVibrators.size(); i++) { 351 mVibrators.valueAt(i).reloadVibratorInfoIfNeeded(); 352 } 353 354 synchronized (mLock) { 355 mVibratorInfos = transformAllVibratorsLocked(VibratorController::getVibratorInfo); 356 VibratorInfo[] infos = new VibratorInfo[mVibratorInfos.size()]; 357 for (int i = 0; i < mVibratorInfos.size(); i++) { 358 infos[i] = mVibratorInfos.valueAt(i); 359 } 360 mCombinedVibratorInfo = VibratorInfoFactory.create(/* id= */ -1, infos); 361 } 362 363 mVibrationSettings.onSystemReady(); 364 mInputDeviceDelegate.onSystemReady(); 365 366 mVibrationSettings.addListener(this::updateServiceState); 367 368 // Will update settings and input devices. 369 updateServiceState(); 370 } finally { 371 synchronized (mLock) { 372 mServiceReady = true; 373 } 374 Slog.v(TAG, "VibratorManager service initialized"); 375 Trace.traceEnd(TRACE_TAG_VIBRATOR); 376 } 377 } 378 379 @Override // Binder call getVibratorIds()380 public int[] getVibratorIds() { 381 return Arrays.copyOf(mVibratorIds, mVibratorIds.length); 382 } 383 384 @Override // Binder call getCapabilities()385 public int getCapabilities() { 386 return (int) mCapabilities; 387 } 388 389 @Override // Binder call 390 @Nullable getVibratorInfo(int vibratorId)391 public VibratorInfo getVibratorInfo(int vibratorId) { 392 final VibratorController controller = mVibrators.get(vibratorId); 393 if (controller == null) { 394 return null; 395 } 396 final VibratorInfo info = controller.getVibratorInfo(); 397 synchronized (mLock) { 398 if (mServiceReady) { 399 return info; 400 } 401 } 402 // If the service is not ready and the load was unsuccessful then return null while waiting 403 // for the service to be ready. It will retry to load the complete info from the HAL. 404 return controller.isVibratorInfoLoadSuccessful() ? info : null; 405 } 406 407 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) 408 @Override // Binder call isVibrating(int vibratorId)409 public boolean isVibrating(int vibratorId) { 410 isVibrating_enforcePermission(); 411 VibratorController controller = mVibrators.get(vibratorId); 412 return controller != null && controller.isVibrating(); 413 } 414 415 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) 416 @Override // Binder call registerVibratorStateListener(int vibratorId, IVibratorStateListener listener)417 public boolean registerVibratorStateListener(int vibratorId, IVibratorStateListener listener) { 418 registerVibratorStateListener_enforcePermission(); 419 VibratorController controller = mVibrators.get(vibratorId); 420 if (controller == null) { 421 return false; 422 } 423 return controller.registerVibratorStateListener(listener); 424 } 425 426 @android.annotation.EnforcePermission(android.Manifest.permission.ACCESS_VIBRATOR_STATE) 427 @Override // Binder call unregisterVibratorStateListener(int vibratorId, IVibratorStateListener listener)428 public boolean unregisterVibratorStateListener(int vibratorId, 429 IVibratorStateListener listener) { 430 unregisterVibratorStateListener_enforcePermission(); 431 VibratorController controller = mVibrators.get(vibratorId); 432 if (controller == null) { 433 return false; 434 } 435 return controller.unregisterVibratorStateListener(listener); 436 } 437 438 @Override // Binder call setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, @Nullable CombinedVibration effect, @Nullable VibrationAttributes attrs)439 public boolean setAlwaysOnEffect(int uid, String opPkg, int alwaysOnId, 440 @Nullable CombinedVibration effect, @Nullable VibrationAttributes attrs) { 441 Trace.traceBegin(TRACE_TAG_VIBRATOR, "setAlwaysOnEffect"); 442 try { 443 mContext.enforceCallingOrSelfPermission( 444 android.Manifest.permission.VIBRATE_ALWAYS_ON, 445 "setAlwaysOnEffect"); 446 447 if (effect == null) { 448 synchronized (mLock) { 449 mAlwaysOnEffects.delete(alwaysOnId); 450 onAllVibratorsLocked(v -> { 451 if (v.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { 452 v.updateAlwaysOn(alwaysOnId, /* effect= */ null); 453 } 454 }); 455 } 456 return true; 457 } 458 if (!isEffectValid(effect)) { 459 return false; 460 } 461 attrs = fixupVibrationAttributes(attrs, effect); 462 synchronized (mLock) { 463 SparseArray<PrebakedSegment> effects = fixupAlwaysOnEffectsLocked(effect); 464 if (effects == null) { 465 // Invalid effects set in CombinedVibrationEffect, or always-on capability is 466 // missing on individual vibrators. 467 return false; 468 } 469 AlwaysOnVibration alwaysOnVibration = new AlwaysOnVibration(alwaysOnId, 470 new CallerInfo(attrs, uid, Context.DEVICE_ID_DEFAULT, opPkg, null), 471 effects); 472 mAlwaysOnEffects.put(alwaysOnId, alwaysOnVibration); 473 updateAlwaysOnLocked(alwaysOnVibration); 474 } 475 return true; 476 } finally { 477 Trace.traceEnd(TRACE_TAG_VIBRATOR); 478 } 479 } 480 481 @Override // Binder call vibrate(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, String reason, IBinder token)482 public void vibrate(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, 483 @Nullable VibrationAttributes attrs, String reason, IBinder token) { 484 Trace.traceBegin(TRACE_TAG_VIBRATOR, "vibrate"); 485 try { 486 vibrateWithPermissionCheck(uid, deviceId, opPkg, effect, attrs, reason, token); 487 } finally { 488 Trace.traceEnd(TRACE_TAG_VIBRATOR); 489 } 490 } 491 492 @Override // Binder call performHapticFeedback(int uid, int deviceId, String opPkg, int constant, String reason, int flags, int privFlags)493 public void performHapticFeedback(int uid, int deviceId, String opPkg, int constant, 494 String reason, int flags, int privFlags) { 495 Trace.traceBegin(TRACE_TAG_VIBRATOR, "performHapticFeedback"); 496 // Note that the `performHapticFeedback` method does not take a token argument from the 497 // caller, and instead, uses this service as the token. This is to mitigate performance 498 // impact that would otherwise be caused due to marshal latency. Haptic feedback effects are 499 // short-lived, so we don't need to cancel when the process dies. 500 try { 501 performHapticFeedbackInternal(uid, deviceId, opPkg, constant, reason, /* token= */ 502 this, flags, privFlags); 503 } finally { 504 Trace.traceEnd(TRACE_TAG_VIBRATOR); 505 } 506 } 507 508 @Override // Binder call performHapticFeedbackForInputDevice(int uid, int deviceId, String opPkg, int constant, int inputDeviceId, int inputSource, String reason, int flags, int privFlags)509 public void performHapticFeedbackForInputDevice(int uid, int deviceId, String opPkg, 510 int constant, int inputDeviceId, int inputSource, String reason, int flags, 511 int privFlags) { 512 Trace.traceBegin(TRACE_TAG_VIBRATOR, "performHapticFeedbackForInputDevice"); 513 try { 514 performHapticFeedbackForInputDeviceInternal(uid, deviceId, opPkg, constant, 515 inputDeviceId, 516 inputSource, reason, /* token= */ this, flags, privFlags); 517 } finally { 518 Trace.traceEnd(TRACE_TAG_VIBRATOR); 519 } 520 } 521 522 /** 523 * An internal-only version of performHapticFeedback that allows the caller access to the 524 * {@link HalVibration}. 525 * The Vibration is only returned if it is ongoing after this method returns. 526 */ 527 @VisibleForTesting 528 @Nullable performHapticFeedbackInternal( int uid, int deviceId, String opPkg, int constant, String reason, IBinder token, int flags, int privFlags)529 HalVibration performHapticFeedbackInternal( 530 int uid, int deviceId, String opPkg, int constant, String reason, 531 IBinder token, int flags, int privFlags) { 532 // Make sure we report the constant id in the requested haptic feedback reason. 533 reason = "performHapticFeedback(constant=" + constant + "): " + reason; 534 HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider(); 535 Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, hapticVibrationProvider); 536 if (ignoreStatus != null) { 537 logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, ignoreStatus); 538 return null; 539 } 540 return performHapticFeedbackWithEffect(uid, deviceId, opPkg, constant, reason, token, 541 hapticVibrationProvider.getVibration(constant), 542 hapticVibrationProvider.getVibrationAttributes( 543 constant, flags, privFlags)); 544 } 545 546 /** 547 * An internal-only version of performHapticFeedback that allows the caller access to the 548 * {@link HalVibration}. 549 * The Vibration is only returned if it is ongoing after this method returns. 550 */ 551 @VisibleForTesting 552 @Nullable performHapticFeedbackForInputDeviceInternal( int uid, int deviceId, String opPkg, int constant, int inputDeviceId, int inputSource, String reason, IBinder token, int flags, int privFlags)553 HalVibration performHapticFeedbackForInputDeviceInternal( 554 int uid, int deviceId, String opPkg, int constant, int inputDeviceId, int inputSource, 555 String reason, IBinder token, int flags, int privFlags) { 556 // Make sure we report the constant id in the requested haptic feedback reason. 557 reason = "performHapticFeedbackForInputDevice(constant=" + constant + ", inputDeviceId=" 558 + inputDeviceId + ", inputSource=" + inputSource + "): " + reason; 559 HapticFeedbackVibrationProvider hapticVibrationProvider = getHapticVibrationProvider(); 560 Status ignoreStatus = shouldIgnoreHapticFeedback(constant, reason, hapticVibrationProvider); 561 if (ignoreStatus != null) { 562 logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, ignoreStatus); 563 return null; 564 } 565 return performHapticFeedbackWithEffect(uid, deviceId, opPkg, constant, reason, token, 566 hapticVibrationProvider.getVibration(constant, inputSource), 567 hapticVibrationProvider.getVibrationAttributes(constant, inputSource, flags, 568 privFlags)); 569 } 570 performHapticFeedbackWithEffect(int uid, int deviceId, String opPkg, int constant, String reason, IBinder token, VibrationEffect effect, VibrationAttributes attrs)571 private HalVibration performHapticFeedbackWithEffect(int uid, int deviceId, String opPkg, 572 int constant, String reason, IBinder token, VibrationEffect effect, 573 VibrationAttributes attrs) { 574 if (effect == null) { 575 logAndRecordPerformHapticFeedbackAttempt(uid, deviceId, opPkg, reason, 576 Status.IGNORED_UNSUPPORTED); 577 Slog.w(TAG, 578 "performHapticFeedbackWithEffect; vibration absent for constant " + constant); 579 return null; 580 } 581 CombinedVibration vib = CombinedVibration.createParallel(effect); 582 VibratorFrameworkStatsLogger.logPerformHapticsFeedbackIfKeyboard(uid, constant); 583 return vibrateWithoutPermissionCheck(uid, deviceId, opPkg, vib, attrs, reason, token); 584 } 585 586 /** 587 * An internal-only version of vibrate that allows the caller access to the 588 * {@link HalVibration}. 589 * The Vibration is only returned if it is ongoing after this method returns. 590 */ 591 @VisibleForTesting 592 @Nullable vibrateWithPermissionCheck(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, String reason, IBinder token)593 HalVibration vibrateWithPermissionCheck(int uid, int deviceId, String opPkg, 594 @NonNull CombinedVibration effect, @Nullable VibrationAttributes attrs, 595 String reason, IBinder token) { 596 attrs = fixupVibrationAttributes(attrs, effect); 597 mContext.enforceCallingOrSelfPermission( 598 android.Manifest.permission.VIBRATE, "vibrate"); 599 return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token); 600 } 601 vibrateWithoutPermissionCheck(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, String reason, IBinder token)602 HalVibration vibrateWithoutPermissionCheck(int uid, int deviceId, String opPkg, 603 @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, 604 String reason, IBinder token) { 605 return vibrateInternal(uid, deviceId, opPkg, effect, attrs, reason, token); 606 } 607 vibrateInternal(int uid, int deviceId, String opPkg, @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, String reason, IBinder token)608 private HalVibration vibrateInternal(int uid, int deviceId, String opPkg, 609 @NonNull CombinedVibration effect, @NonNull VibrationAttributes attrs, 610 String reason, IBinder token) { 611 CallerInfo callerInfo = new CallerInfo(attrs, uid, deviceId, opPkg, reason); 612 if (token == null) { 613 Slog.e(TAG, "token must not be null"); 614 logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_ERROR_TOKEN); 615 return null; 616 } 617 enforceUpdateAppOpsStatsPermission(uid); 618 if (!isEffectValid(effect)) { 619 logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_UNSUPPORTED); 620 return null; 621 } 622 if (effect.hasVendorEffects()) { 623 if (!Flags.vendorVibrationEffects()) { 624 Slog.e(TAG, "vibrate; vendor effects feature disabled"); 625 logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_UNSUPPORTED); 626 return null; 627 } 628 if (!hasPermission(android.Manifest.permission.VIBRATE_VENDOR_EFFECTS)) { 629 Slog.e(TAG, "vibrate; no permission for vendor effects"); 630 logAndRecordVibrationAttempt(effect, callerInfo, Status.IGNORED_MISSING_PERMISSION); 631 return null; 632 } 633 } 634 // Create Vibration.Stats as close to the received request as possible, for tracking. 635 SingleVibrationSession session = new SingleVibrationSession(token, callerInfo, effect); 636 HalVibration vib = session.getVibration(); 637 vib.fillFallbacks(mVibrationSettings::getFallbackEffect); 638 639 if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) { 640 // Force update of user settings before checking if this vibration effect should 641 // be ignored or scaled. 642 mVibrationSettings.update(); 643 } 644 645 synchronized (mLock) { 646 if (DEBUG) { 647 Slog.d(TAG, "Starting vibrate for vibration " + vib.id); 648 } 649 650 // Check if user settings or DnD is set to ignore this vibration. 651 Status ignoreStatus = shouldIgnoreVibrationLocked(callerInfo); 652 CallerInfo ignoredBy = null; 653 654 // Check if ongoing vibration is more important than this vibration. 655 if (ignoreStatus == null) { 656 Vibration.EndInfo vibrationEndInfo = shouldIgnoreForOngoingLocked(session); 657 if (vibrationEndInfo != null) { 658 ignoreStatus = vibrationEndInfo.status; 659 ignoredBy = vibrationEndInfo.endedBy; 660 } 661 } 662 663 // If not ignored so far then try to start this vibration. 664 if (ignoreStatus == null) { 665 // TODO(b/378492007): Investigate if we can move this around AppOpsManager calls 666 final long ident = Binder.clearCallingIdentity(); 667 try { 668 if (mCurrentSession != null) { 669 if (shouldPipelineVibrationLocked(mCurrentSession, vib)) { 670 // Don't cancel the current vibration if it's pipeline-able. 671 // Note that if there is a pending next vibration that can't be 672 // pipelined, it will have already cancelled the current one, so we 673 // don't need to consider it here as well. 674 if (DEBUG) { 675 Slog.d(TAG, "Pipelining vibration " + vib.id); 676 } 677 } else { 678 vib.stats.reportInterruptedAnotherVibration( 679 mCurrentSession.getCallerInfo()); 680 mCurrentSession.requestEnd(Status.CANCELLED_SUPERSEDED, callerInfo, 681 /* immediate= */ false); 682 } 683 } 684 clearNextSessionLocked(Status.CANCELLED_SUPERSEDED, callerInfo); 685 ignoreStatus = startVibrationLocked(session); 686 } finally { 687 Binder.restoreCallingIdentity(ident); 688 } 689 } 690 691 // Ignored or failed to start the vibration, end it and report metrics right away. 692 if (ignoreStatus != null) { 693 endSessionLocked(session, ignoreStatus, ignoredBy); 694 } 695 return vib; 696 } 697 } 698 699 @Override // Binder call cancelVibrate(int usageFilter, IBinder token)700 public void cancelVibrate(int usageFilter, IBinder token) { 701 Trace.traceBegin(TRACE_TAG_VIBRATOR, "cancelVibrate"); 702 try { 703 mContext.enforceCallingOrSelfPermission( 704 android.Manifest.permission.VIBRATE, 705 "cancelVibrate"); 706 707 synchronized (mLock) { 708 if (DEBUG) { 709 Slog.d(TAG, "Canceling vibration"); 710 } 711 // TODO(b/378492007): Investigate if we can move this around AppOpsManager calls 712 final long ident = Binder.clearCallingIdentity(); 713 try { 714 // TODO(b/370948466): investigate why token not checked on external vibrations. 715 IBinder cancelToken = 716 (mNextSession instanceof ExternalVibrationSession) ? null : token; 717 if (shouldCancelSession(mNextSession, usageFilter, cancelToken)) { 718 clearNextSessionLocked(Status.CANCELLED_BY_USER); 719 } 720 cancelToken = 721 (mCurrentSession instanceof ExternalVibrationSession) ? null : token; 722 if (shouldCancelSession(mCurrentSession, usageFilter, cancelToken)) { 723 mCurrentSession.requestEnd(Status.CANCELLED_BY_USER); 724 } 725 } finally { 726 Binder.restoreCallingIdentity(ident); 727 } 728 } 729 } finally { 730 Trace.traceEnd(TRACE_TAG_VIBRATOR); 731 } 732 } 733 734 @android.annotation.EnforcePermission(allOf = { 735 android.Manifest.permission.VIBRATE, 736 android.Manifest.permission.VIBRATE_VENDOR_EFFECTS, 737 android.Manifest.permission.START_VIBRATION_SESSIONS, 738 }) 739 @Override // Binder call startVendorVibrationSession(int uid, int deviceId, String opPkg, int[] vibratorIds, VibrationAttributes attrs, String reason, IVibrationSessionCallback callback)740 public ICancellationSignal startVendorVibrationSession(int uid, int deviceId, String opPkg, 741 int[] vibratorIds, VibrationAttributes attrs, String reason, 742 IVibrationSessionCallback callback) { 743 startVendorVibrationSession_enforcePermission(); 744 Trace.traceBegin(TRACE_TAG_VIBRATOR, "startVibrationSession"); 745 try { 746 VendorVibrationSession session = startVendorVibrationSessionInternal( 747 uid, deviceId, opPkg, vibratorIds, attrs, reason, callback); 748 return session == null ? null : session.getCancellationSignal(); 749 } finally { 750 Trace.traceEnd(TRACE_TAG_VIBRATOR); 751 } 752 } 753 startVendorVibrationSessionInternal(int uid, int deviceId, String opPkg, int[] vibratorIds, VibrationAttributes attrs, String reason, IVibrationSessionCallback callback)754 VendorVibrationSession startVendorVibrationSessionInternal(int uid, int deviceId, String opPkg, 755 int[] vibratorIds, VibrationAttributes attrs, String reason, 756 IVibrationSessionCallback callback) { 757 if (!Flags.vendorVibrationEffects()) { 758 throw new UnsupportedOperationException("Vibration sessions not supported"); 759 } 760 attrs = fixupVibrationAttributes(attrs, /* effect= */ null); 761 CallerInfo callerInfo = new CallerInfo(attrs, uid, deviceId, opPkg, reason); 762 if (callback == null) { 763 Slog.e(TAG, "session callback must not be null"); 764 logAndRecordSessionAttempt(callerInfo, Status.IGNORED_ERROR_TOKEN); 765 return null; 766 } 767 if (vibratorIds == null) { 768 vibratorIds = new int[0]; 769 } 770 enforceUpdateAppOpsStatsPermission(uid); 771 772 // Create session with adapter that only uses the session vibrators. 773 SparseArray<VibratorController> sessionVibrators = new SparseArray<>(vibratorIds.length); 774 for (int vibratorId : vibratorIds) { 775 VibratorController controller = mVibrators.get(vibratorId); 776 if (controller != null) { 777 sessionVibrators.put(vibratorId, controller); 778 } 779 } 780 DeviceAdapter deviceAdapter = new DeviceAdapter(mVibrationSettings, sessionVibrators); 781 VendorVibrationSession session = new VendorVibrationSession(callerInfo, mHandler, 782 mVendorVibrationSessionCallbacks, deviceAdapter, callback); 783 784 if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) { 785 // Force update of user settings before checking if this vibration effect should 786 // be ignored or scaled. 787 mVibrationSettings.update(); 788 } 789 790 synchronized (mLock) { 791 if (DEBUG) { 792 Slog.d(TAG, "Starting vendor session " + session.getSessionId()); 793 } 794 795 Status ignoreStatus = null; 796 CallerInfo ignoredBy = null; 797 798 // Check if HAL has capability to start sessions. 799 if ((mCapabilities & IVibratorManager.CAP_START_SESSIONS) == 0) { 800 if (DEBUG) { 801 Slog.d(TAG, "Missing capability to start sessions, ignoring request"); 802 } 803 ignoreStatus = Status.IGNORED_UNSUPPORTED; 804 } 805 806 // Check if vibrator IDs requested are available. 807 if (ignoreStatus == null) { 808 if (vibratorIds.length == 0 809 || vibratorIds.length != deviceAdapter.getAvailableVibratorIds().length) { 810 Slog.e(TAG, "Bad vibrator ids to start session, ignoring request." 811 + " requested=" + Arrays.toString(vibratorIds) 812 + " available=" + Arrays.toString(mVibratorIds)); 813 ignoreStatus = Status.IGNORED_UNSUPPORTED; 814 } 815 } 816 817 // Check if user settings or DnD is set to ignore this session. 818 if (ignoreStatus == null) { 819 ignoreStatus = shouldIgnoreVibrationLocked(callerInfo); 820 } 821 822 // Check if ongoing vibration is more important than this session. 823 if (ignoreStatus == null) { 824 Vibration.EndInfo vibrationEndInfo = shouldIgnoreForOngoingLocked(session); 825 if (vibrationEndInfo != null) { 826 ignoreStatus = vibrationEndInfo.status; 827 ignoredBy = vibrationEndInfo.endedBy; 828 } 829 } 830 831 if (ignoreStatus == null) { 832 // TODO(b/378492007): Investigate if we can move this around AppOpsManager calls 833 final long ident = Binder.clearCallingIdentity(); 834 try { 835 // If not ignored so far then stop ongoing sessions before starting this one. 836 clearNextSessionLocked(Status.CANCELLED_SUPERSEDED, callerInfo); 837 if (mCurrentSession != null) { 838 mNextSession = session; 839 mCurrentSession.requestEnd(Status.CANCELLED_SUPERSEDED, callerInfo, 840 /* immediate= */ false); 841 } else { 842 ignoreStatus = startVendorSessionLocked(session); 843 } 844 } finally { 845 Binder.restoreCallingIdentity(ident); 846 } 847 } 848 849 // Ignored or failed to start the session, end it and report metrics right away. 850 if (ignoreStatus != null) { 851 endSessionLocked(session, ignoreStatus, ignoredBy); 852 } 853 return session; 854 } 855 } 856 857 @GuardedBy("mLock") 858 @Nullable startVendorSessionLocked(VendorVibrationSession session)859 private Status startVendorSessionLocked(VendorVibrationSession session) { 860 Trace.traceBegin(TRACE_TAG_VIBRATOR, "startSessionLocked"); 861 try { 862 long sessionId = session.getSessionId(); 863 if (DEBUG) { 864 Slog.d(TAG, "Starting session " + sessionId + " in HAL"); 865 } 866 if (session.isEnded()) { 867 // Session already ended, possibly cancelled by app cancellation signal. 868 return session.getStatus(); 869 } 870 CallerInfo callerInfo = session.getCallerInfo(); 871 int mode = startAppOpModeLocked(callerInfo); 872 switch (mode) { 873 case AppOpsManager.MODE_ALLOWED: 874 Trace.asyncTraceBegin(TRACE_TAG_VIBRATOR, "vibration", 0); 875 // Make sure mCurrentVibration is set while triggering the HAL. 876 mCurrentSession = session; 877 if (!session.linkToDeath()) { 878 // Shouldn't happen. The method call already logs. 879 finishAppOpModeLocked(callerInfo); 880 mCurrentSession = null; 881 return Status.IGNORED_ERROR_TOKEN; 882 } 883 if (!mNativeWrapper.startSession(sessionId, session.getVibratorIds())) { 884 Slog.e(TAG, "Error starting session " + sessionId + " on vibrators " 885 + Arrays.toString(session.getVibratorIds())); 886 session.unlinkToDeath(); 887 finishAppOpModeLocked(callerInfo); 888 mCurrentSession = null; 889 return Status.IGNORED_UNSUPPORTED; 890 } 891 session.notifyStart(); 892 return null; 893 case AppOpsManager.MODE_ERRORED: 894 Slog.w(TAG, "Start AppOpsManager operation errored for uid " + callerInfo.uid); 895 return Status.IGNORED_ERROR_APP_OPS; 896 default: 897 return Status.IGNORED_APP_OPS; 898 } 899 } finally { 900 Trace.traceEnd(TRACE_TAG_VIBRATOR); 901 } 902 } 903 904 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)905 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 906 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 907 908 final long ident = Binder.clearCallingIdentity(); 909 910 boolean isDumpProto = false; 911 for (String arg : args) { 912 if (arg.equals("--proto")) { 913 isDumpProto = true; 914 } 915 } 916 try { 917 if (isDumpProto) { 918 dumpProto(fd); 919 } else { 920 dumpText(pw); 921 } 922 } finally { 923 Binder.restoreCallingIdentity(ident); 924 } 925 } 926 dumpText(PrintWriter w)927 private void dumpText(PrintWriter w) { 928 if (DEBUG) { 929 Slog.d(TAG, "Dumping vibrator manager service to text..."); 930 } 931 IndentingPrintWriter pw = new IndentingPrintWriter(w, /* singleIndent= */ " "); 932 synchronized (mLock) { 933 pw.println("VibratorManagerService:"); 934 pw.increaseIndent(); 935 936 mVibrationSettings.dump(pw); 937 pw.println(); 938 939 mVibrationScaler.dump(pw); 940 pw.println(); 941 942 pw.println("Vibrators:"); 943 pw.increaseIndent(); 944 for (int i = 0; i < mVibrators.size(); i++) { 945 mVibrators.valueAt(i).dump(pw); 946 } 947 pw.decreaseIndent(); 948 pw.println(); 949 950 pw.println("CurrentVibration:"); 951 pw.increaseIndent(); 952 if (mCurrentSession != null) { 953 mCurrentSession.getDebugInfo().dump(pw); 954 } else { 955 pw.println("null"); 956 } 957 pw.decreaseIndent(); 958 pw.println(); 959 960 pw.println("NextVibration:"); 961 pw.increaseIndent(); 962 if (mNextSession != null) { 963 mNextSession.getDebugInfo().dump(pw); 964 } else { 965 pw.println("null"); 966 } 967 pw.decreaseIndent(); 968 } 969 970 pw.println(); 971 pw.println(); 972 mVibratorManagerRecords.dump(pw); 973 974 pw.println(); 975 pw.println(); 976 mVibratorControlService.dump(pw); 977 } 978 dumpProto(FileDescriptor fd)979 private void dumpProto(FileDescriptor fd) { 980 final ProtoOutputStream proto = new ProtoOutputStream(fd); 981 if (DEBUG) { 982 Slog.d(TAG, "Dumping vibrator manager service to proto..."); 983 } 984 synchronized (mLock) { 985 mVibrationSettings.dump(proto); 986 mVibrationScaler.dump(proto); 987 if (mCurrentSession != null) { 988 mCurrentSession.getDebugInfo().dump(proto, 989 VibratorManagerServiceDumpProto.CURRENT_VIBRATION); 990 } 991 for (int i = 0; i < mVibrators.size(); i++) { 992 proto.write(VibratorManagerServiceDumpProto.VIBRATOR_IDS, mVibrators.keyAt(i)); 993 } 994 } 995 mVibratorManagerRecords.dump(proto); 996 mVibratorControlService.dump(proto); 997 proto.flush(); 998 } 999 1000 @Override onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback cb, ResultReceiver resultReceiver)1001 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 1002 String[] args, ShellCallback cb, ResultReceiver resultReceiver) { 1003 new VibratorManagerShellCommand(cb.getShellCallbackBinder()) 1004 .exec(this, in, out, err, args, cb, resultReceiver); 1005 } 1006 1007 @VisibleForTesting updateServiceState()1008 void updateServiceState() { 1009 synchronized (mLock) { 1010 if (DEBUG) { 1011 Slog.d(TAG, "Updating device state..."); 1012 } 1013 boolean inputDevicesChanged = mInputDeviceDelegate.updateInputDeviceVibrators( 1014 mVibrationSettings.shouldVibrateInputDevices()); 1015 1016 for (int i = 0; i < mAlwaysOnEffects.size(); i++) { 1017 updateAlwaysOnLocked(mAlwaysOnEffects.valueAt(i)); 1018 } 1019 1020 if (mCurrentSession == null) { 1021 return; 1022 } 1023 1024 if (!Flags.fixExternalVibrationSystemUpdateAware() 1025 && (mCurrentSession instanceof ExternalVibrationSession)) { 1026 return; 1027 } 1028 1029 Status ignoreStatus = shouldIgnoreVibrationLocked(mCurrentSession.getCallerInfo()); 1030 if (inputDevicesChanged || (ignoreStatus != null)) { 1031 if (DEBUG) { 1032 Slog.d(TAG, "Canceling vibration because settings changed: " 1033 + (ignoreStatus == null ? "input devices changed" : ignoreStatus)); 1034 } 1035 mCurrentSession.requestEnd(Status.CANCELLED_BY_SETTINGS_UPDATE); 1036 } 1037 } 1038 } 1039 setExternalControl(boolean externalControl, VibrationStats vibrationStats)1040 private void setExternalControl(boolean externalControl, VibrationStats vibrationStats) { 1041 for (int i = 0; i < mVibrators.size(); i++) { 1042 mVibrators.valueAt(i).setExternalControl(externalControl); 1043 vibrationStats.reportSetExternalControl(); 1044 } 1045 } 1046 1047 @GuardedBy("mLock") updateAlwaysOnLocked(AlwaysOnVibration vib)1048 private void updateAlwaysOnLocked(AlwaysOnVibration vib) { 1049 for (int i = 0; i < vib.effects.size(); i++) { 1050 VibratorController vibrator = mVibrators.get(vib.effects.keyAt(i)); 1051 PrebakedSegment effect = vib.effects.valueAt(i); 1052 if (vibrator == null) { 1053 continue; 1054 } 1055 Status ignoreStatus = shouldIgnoreVibrationLocked(vib.callerInfo); 1056 if (ignoreStatus == null) { 1057 effect = mVibrationScaler.scale(effect, vib.callerInfo.attrs.getUsage()); 1058 } else { 1059 // Vibration should not run, use null effect to remove registered effect. 1060 effect = null; 1061 } 1062 vibrator.updateAlwaysOn(vib.alwaysOnId, effect); 1063 } 1064 } 1065 1066 @GuardedBy("mLock") 1067 @Nullable startVibrationLocked(SingleVibrationSession session)1068 private Status startVibrationLocked(SingleVibrationSession session) { 1069 Trace.traceBegin(TRACE_TAG_VIBRATOR, "startVibrationLocked"); 1070 try { 1071 if (mInputDeviceDelegate.isAvailable()) { 1072 return startVibrationOnInputDevicesLocked(session.getVibration()); 1073 } 1074 if (mCurrentSession == null) { 1075 return startVibrationOnThreadLocked(session); 1076 } 1077 // If there's already a vibration queued (waiting for the previous one to finish 1078 // cancelling), end it cleanly and replace it with the new one. 1079 // Note that we don't consider pipelining here, because new pipelined ones should 1080 // replace pending non-executing pipelined ones anyway. 1081 clearNextSessionLocked(Status.IGNORED_SUPERSEDED, session.getCallerInfo()); 1082 mNextSession = session; 1083 return null; 1084 } finally { 1085 Trace.traceEnd(TRACE_TAG_VIBRATOR); 1086 } 1087 } 1088 1089 @GuardedBy("mLock") 1090 @Nullable startVibrationOnThreadLocked(SingleVibrationSession session)1091 private Status startVibrationOnThreadLocked(SingleVibrationSession session) { 1092 if (DEBUG) { 1093 Slog.d(TAG, "Starting vibration " + session.getVibration().id + " on thread"); 1094 } 1095 VibrationStepConductor conductor = createVibrationStepConductor(session.getVibration()); 1096 session.setVibrationConductor(conductor); 1097 CallerInfo callerInfo = session.getCallerInfo(); 1098 int mode = startAppOpModeLocked(callerInfo); 1099 switch (mode) { 1100 case AppOpsManager.MODE_ALLOWED: 1101 Trace.asyncTraceBegin(TRACE_TAG_VIBRATOR, "vibration", 0); 1102 // Make sure mCurrentVibration is set while triggering the VibrationThread. 1103 mCurrentSession = session; 1104 if (!mCurrentSession.linkToDeath()) { 1105 // Shouldn't happen. The method call already logs. 1106 finishAppOpModeLocked(callerInfo); 1107 mCurrentSession = null; // Aborted. 1108 return Status.IGNORED_ERROR_TOKEN; 1109 } 1110 if (!mVibrationThread.runVibrationOnVibrationThread(conductor)) { 1111 // Shouldn't happen. The method call already logs. 1112 session.setVibrationConductor(null); // Rejected by thread, clear it in session. 1113 mCurrentSession.unlinkToDeath(); 1114 finishAppOpModeLocked(callerInfo); 1115 mCurrentSession = null; // Aborted. 1116 return Status.IGNORED_ERROR_SCHEDULING; 1117 } 1118 return null; 1119 case AppOpsManager.MODE_ERRORED: 1120 Slog.w(TAG, "Start AppOpsManager operation errored for uid " + callerInfo.uid); 1121 return Status.IGNORED_ERROR_APP_OPS; 1122 default: 1123 return Status.IGNORED_APP_OPS; 1124 } 1125 } 1126 1127 @GuardedBy("mLock") maybeStartNextSessionLocked()1128 private void maybeStartNextSessionLocked() { 1129 if (mNextSession instanceof SingleVibrationSession session) { 1130 mNextSession = null; 1131 Status errorStatus = startVibrationOnThreadLocked(session); 1132 if (errorStatus != null) { 1133 if (DEBUG) { 1134 Slog.d(TAG, "Error starting next vibration " + session.getVibration().id); 1135 } 1136 endSessionLocked(session, errorStatus); 1137 } 1138 } else if (mNextSession instanceof VendorVibrationSession session) { 1139 mNextSession = null; 1140 Status errorStatus = startVendorSessionLocked(session); 1141 if (errorStatus != null) { 1142 if (DEBUG) { 1143 Slog.d(TAG, "Error starting next session " + session.getSessionId()); 1144 } 1145 endSessionLocked(session, errorStatus); 1146 } 1147 } // External vibrations cannot be started asynchronously. 1148 } 1149 1150 @GuardedBy("mLock") endSessionLocked(VibrationSession session, Status status)1151 private void endSessionLocked(VibrationSession session, Status status) { 1152 endSessionLocked(session, status, /* endedBy= */ null); 1153 } 1154 1155 @GuardedBy("mLock") endSessionLocked(VibrationSession session, Status status, CallerInfo endedBy)1156 private void endSessionLocked(VibrationSession session, Status status, CallerInfo endedBy) { 1157 session.requestEnd(status, endedBy, /* immediate= */ false); 1158 logAndRecordVibration(session.getDebugInfo()); 1159 } 1160 createVibrationStepConductor(HalVibration vib)1161 private VibrationStepConductor createVibrationStepConductor(HalVibration vib) { 1162 return createVibrationStepConductor(vib, mDeviceAdapter, /* isInSession= */ false); 1163 } 1164 createSessionVibrationStepConductor(HalVibration vib, DeviceAdapter deviceAdapter)1165 private VibrationStepConductor createSessionVibrationStepConductor(HalVibration vib, 1166 DeviceAdapter deviceAdapter) { 1167 return createVibrationStepConductor(vib, deviceAdapter, /* isInSession= */ true); 1168 } 1169 createVibrationStepConductor(HalVibration vib, DeviceAdapter deviceAdapter, boolean isInSession)1170 private VibrationStepConductor createVibrationStepConductor(HalVibration vib, 1171 DeviceAdapter deviceAdapter, boolean isInSession) { 1172 CompletableFuture<Void> requestVibrationParamsFuture = null; 1173 1174 if (Flags.adaptiveHapticsEnabled() 1175 && mVibratorControlService.shouldRequestVibrationParams( 1176 vib.callerInfo.attrs.getUsage())) { 1177 requestVibrationParamsFuture = 1178 mVibratorControlService.triggerVibrationParamsRequest( 1179 vib.callerInfo.uid, vib.callerInfo.attrs.getUsage(), 1180 mVibrationSettings.getRequestVibrationParamsTimeoutMs()); 1181 } 1182 1183 return new VibrationStepConductor(vib, isInSession, mVibrationSettings, 1184 deviceAdapter, mVibrationScaler, mFrameworkStatsLogger, 1185 requestVibrationParamsFuture, mVibrationThreadCallbacks); 1186 } 1187 startVibrationOnInputDevicesLocked(HalVibration vib)1188 private Status startVibrationOnInputDevicesLocked(HalVibration vib) { 1189 // Scale resolves the default amplitudes from the effect before scaling them. 1190 vib.scaleEffects(mVibrationScaler); 1191 mInputDeviceDelegate.vibrateIfAvailable(vib.callerInfo, vib.getEffectToPlay()); 1192 return Status.FORWARDED_TO_INPUT_DEVICES; 1193 } 1194 logAndRecordPerformHapticFeedbackAttempt(int uid, int deviceId, String opPkg, String reason, Status status)1195 private void logAndRecordPerformHapticFeedbackAttempt(int uid, int deviceId, String opPkg, 1196 String reason, Status status) { 1197 CallerInfo callerInfo = new CallerInfo( 1198 VibrationAttributes.createForUsage(VibrationAttributes.USAGE_UNKNOWN), 1199 uid, deviceId, opPkg, reason); 1200 logAndRecordVibrationAttempt(/* effect= */ null, callerInfo, status); 1201 } 1202 logAndRecordVibrationAttempt(@ullable CombinedVibration effect, CallerInfo callerInfo, Status status)1203 private void logAndRecordVibrationAttempt(@Nullable CombinedVibration effect, 1204 CallerInfo callerInfo, Status status) { 1205 logAndRecordVibration(createVibrationAttemptDebugInfo(effect, callerInfo, status)); 1206 } 1207 logAndRecordSessionAttempt(CallerInfo callerInfo, Status status)1208 private void logAndRecordSessionAttempt(CallerInfo callerInfo, Status status) { 1209 logAndRecordVibration( 1210 new VendorVibrationSession.DebugInfoImpl(status, callerInfo, 1211 SystemClock.uptimeMillis(), System.currentTimeMillis(), 1212 /* startTime= */ 0, /* endUptime= */ 0, /* endTime= */ 0, 1213 /* endedByVendor= */ false, /* vibrations= */ null)); 1214 } 1215 logAndRecordVibration(DebugInfo info)1216 private void logAndRecordVibration(DebugInfo info) { 1217 info.logMetrics(mFrameworkStatsLogger); 1218 logVibrationStatus(info.getCallerInfo().uid, info.getCallerInfo().attrs, info.getStatus()); 1219 mVibratorManagerRecords.record(info); 1220 } 1221 createVibrationAttemptDebugInfo(@ullable CombinedVibration effect, CallerInfo callerInfo, Status status)1222 private DebugInfo createVibrationAttemptDebugInfo(@Nullable CombinedVibration effect, 1223 CallerInfo callerInfo, Status status) { 1224 return new Vibration.DebugInfoImpl(status, callerInfo, 1225 VibrationStats.StatsInfo.findVibrationType(effect), new VibrationStats(), 1226 effect, /* originalEffect= */ null, VibrationScaler.SCALE_NONE, 1227 VibrationScaler.ADAPTIVE_SCALE_NONE); 1228 } 1229 logVibrationStatus(int uid, VibrationAttributes attrs, Status status)1230 private void logVibrationStatus(int uid, VibrationAttributes attrs, Status status) { 1231 switch (status) { 1232 case IGNORED_BACKGROUND: 1233 Slog.e(TAG, "Ignoring incoming vibration as process with" 1234 + " uid= " + uid + " is background," + " attrs= " + attrs); 1235 break; 1236 case IGNORED_ERROR_APP_OPS: 1237 Slog.w(TAG, "Would be an error: vibrate from uid " + uid); 1238 break; 1239 case IGNORED_FOR_EXTERNAL: 1240 if (DEBUG) { 1241 Slog.d(TAG, "Ignoring incoming vibration for current external vibration"); 1242 } 1243 break; 1244 case IGNORED_FOR_HIGHER_IMPORTANCE: 1245 if (DEBUG) { 1246 Slog.d(TAG, "Ignoring incoming vibration in favor of ongoing vibration" 1247 + " with higher importance"); 1248 } 1249 break; 1250 case IGNORED_FOR_ONGOING: 1251 if (DEBUG) { 1252 Slog.d(TAG, "Ignoring incoming vibration in favor of repeating vibration"); 1253 } 1254 break; 1255 case IGNORED_FOR_RINGER_MODE: 1256 if (DEBUG) { 1257 Slog.d(TAG, "Ignoring incoming vibration because of ringer mode, attrs=" 1258 + attrs); 1259 } 1260 break; 1261 case IGNORED_FROM_VIRTUAL_DEVICE: 1262 if (DEBUG) { 1263 Slog.d(TAG, "Ignoring incoming vibration because it came from a virtual" 1264 + " device, attrs= " + attrs); 1265 } 1266 break; 1267 default: 1268 if (DEBUG) { 1269 Slog.d(TAG, "Vibration for uid=" + uid + " and with attrs=" + attrs 1270 + " ended with status " + status); 1271 } 1272 } 1273 } 1274 onVibrationSessionComplete(long sessionId)1275 private void onVibrationSessionComplete(long sessionId) { 1276 synchronized (mLock) { 1277 if (mCurrentSession == null || mCurrentSession.getSessionId() != sessionId) { 1278 if (DEBUG) { 1279 Slog.d(TAG, "Vibration session " + sessionId + " callback ignored"); 1280 } 1281 return; 1282 } 1283 if (DEBUG) { 1284 Slog.d(TAG, "Vibration session " + sessionId + " complete, notifying session"); 1285 } 1286 mCurrentSession.notifySessionCallback(); 1287 } 1288 } 1289 onSyncedVibrationComplete(long vibrationId)1290 private void onSyncedVibrationComplete(long vibrationId) { 1291 synchronized (mLock) { 1292 if (mCurrentSession != null) { 1293 if (DEBUG) { 1294 Slog.d(TAG, "Synced vibration " + vibrationId + " complete, notifying thread"); 1295 } 1296 mCurrentSession.notifySyncedVibratorsCallback(vibrationId); 1297 } 1298 } 1299 } 1300 onVibrationComplete(int vibratorId, long vibrationId, long stepId)1301 private void onVibrationComplete(int vibratorId, long vibrationId, long stepId) { 1302 synchronized (mLock) { 1303 if (mCurrentSession != null) { 1304 if (DEBUG) { 1305 Slog.d(TAG, "Vibration " + vibrationId + " step " + stepId 1306 + " on vibrator " + vibratorId + " complete, notifying thread"); 1307 } 1308 mCurrentSession.notifyVibratorCallback(vibratorId, vibrationId, stepId); 1309 } 1310 } 1311 } 1312 1313 /** 1314 * Check if given vibration should be ignored by this service because of the ongoing vibration. 1315 * 1316 * @return a Vibration.EndInfo if the vibration should be ignored, null otherwise. 1317 */ 1318 @GuardedBy("mLock") 1319 @Nullable shouldIgnoreForOngoingLocked(VibrationSession session)1320 private Vibration.EndInfo shouldIgnoreForOngoingLocked(VibrationSession session) { 1321 if (mNextSession != null) { 1322 Vibration.EndInfo vibrationEndInfo = shouldIgnoreForOngoing(session, 1323 mNextSession); 1324 if (vibrationEndInfo != null) { 1325 // Next vibration has higher importance than the new one, so the new vibration 1326 // should be ignored. 1327 return vibrationEndInfo; 1328 } 1329 } 1330 1331 if (mCurrentSession != null) { 1332 if (mCurrentSession.wasEndRequested()) { 1333 // Current session has ended or is cancelling, should not block incoming vibrations. 1334 return null; 1335 } 1336 1337 return shouldIgnoreForOngoing(session, mCurrentSession); 1338 } 1339 1340 return null; 1341 } 1342 1343 /** 1344 * Checks if the ongoing vibration has higher importance than the new one. If they have similar 1345 * importance, then {@link VibrationSession#isRepeating()} is used as a tiebreaker. 1346 * 1347 * @return a Vibration.EndInfo if the vibration should be ignored, null otherwise. 1348 */ 1349 @Nullable shouldIgnoreForOngoing( @onNull VibrationSession newSession, @NonNull VibrationSession ongoingSession)1350 private static Vibration.EndInfo shouldIgnoreForOngoing( 1351 @NonNull VibrationSession newSession, @NonNull VibrationSession ongoingSession) { 1352 1353 int newSessionImportance = getVibrationImportance(newSession); 1354 int ongoingSessionImportance = getVibrationImportance(ongoingSession); 1355 1356 if (newSessionImportance > ongoingSessionImportance) { 1357 // New vibration has higher importance and should not be ignored. 1358 return null; 1359 } 1360 1361 if (ongoingSessionImportance > newSessionImportance) { 1362 // Existing vibration has higher importance and should not be cancelled. 1363 return new Vibration.EndInfo(Status.IGNORED_FOR_HIGHER_IMPORTANCE, 1364 ongoingSession.getCallerInfo()); 1365 } 1366 1367 // Same importance, use repeating as a tiebreaker. 1368 if (ongoingSession.isRepeating() && !newSession.isRepeating()) { 1369 // Ongoing vibration is repeating and new one is not, give priority to ongoing 1370 return new Vibration.EndInfo(Status.IGNORED_FOR_ONGOING, 1371 ongoingSession.getCallerInfo()); 1372 } 1373 // New vibration is repeating or this is a complete tie between them, 1374 // give priority to new vibration. 1375 return null; 1376 } 1377 1378 /** 1379 * Gets the vibration importance based on usage. In the case where usage is unknown, it maps 1380 * repeating vibrations to ringtones and non-repeating vibrations to touches. 1381 * 1382 * @return a numeric representation for the vibration importance, larger values represent a 1383 * higher importance 1384 */ getVibrationImportance(VibrationSession session)1385 private static int getVibrationImportance(VibrationSession session) { 1386 int usage = session.getCallerInfo().attrs.getUsage(); 1387 if (usage == VibrationAttributes.USAGE_UNKNOWN) { 1388 if (session.isRepeating()) { 1389 usage = VibrationAttributes.USAGE_RINGTONE; 1390 } else { 1391 usage = VibrationAttributes.USAGE_TOUCH; 1392 } 1393 } 1394 1395 switch (usage) { 1396 case VibrationAttributes.USAGE_RINGTONE: 1397 return 5; 1398 case VibrationAttributes.USAGE_ALARM: 1399 return 4; 1400 case VibrationAttributes.USAGE_NOTIFICATION: 1401 return 3; 1402 case VibrationAttributes.USAGE_COMMUNICATION_REQUEST: 1403 case VibrationAttributes.USAGE_ACCESSIBILITY: 1404 return 2; 1405 case VibrationAttributes.USAGE_HARDWARE_FEEDBACK: 1406 case VibrationAttributes.USAGE_PHYSICAL_EMULATION: 1407 return 1; 1408 case VibrationAttributes.USAGE_MEDIA: 1409 case VibrationAttributes.USAGE_TOUCH: 1410 default: 1411 return 0; 1412 } 1413 } 1414 1415 /** Returns true if ongoing session should pipeline with the next vibration requested. */ 1416 @GuardedBy("mLock") shouldPipelineVibrationLocked(VibrationSession currentSession, HalVibration nextVibration)1417 private boolean shouldPipelineVibrationLocked(VibrationSession currentSession, 1418 HalVibration nextVibration) { 1419 if (!(currentSession instanceof SingleVibrationSession currentVibration)) { 1420 // Only single vibration session can be pipelined. 1421 return false; 1422 } 1423 return currentVibration.getVibration().canPipelineWith(nextVibration, mVibratorInfos, 1424 mVibrationConfig.getVibrationPipelineMaxDurationMs()); 1425 } 1426 1427 /** 1428 * Check if given vibration should be ignored by this service. 1429 * 1430 * @return a Vibration.EndInfo if the vibration should be ignored, null otherwise. 1431 */ 1432 @GuardedBy("mLock") 1433 @Nullable shouldIgnoreVibrationLocked(CallerInfo callerInfo)1434 private Status shouldIgnoreVibrationLocked(CallerInfo callerInfo) { 1435 Status statusFromSettings = mVibrationSettings.shouldIgnoreVibration(callerInfo); 1436 if (statusFromSettings != null) { 1437 return statusFromSettings; 1438 } 1439 1440 int mode = checkAppOpModeLocked(callerInfo); 1441 if (mode != AppOpsManager.MODE_ALLOWED) { 1442 if (mode == AppOpsManager.MODE_ERRORED) { 1443 // We might be getting calls from within system_server, so we don't actually 1444 // want to throw a SecurityException here. 1445 return Status.IGNORED_ERROR_APP_OPS; 1446 } else { 1447 return Status.IGNORED_APP_OPS; 1448 } 1449 } 1450 1451 return null; 1452 } 1453 1454 @Nullable shouldIgnoreHapticFeedback(int constant, String reason, HapticFeedbackVibrationProvider hapticVibrationProvider)1455 private Status shouldIgnoreHapticFeedback(int constant, String reason, 1456 HapticFeedbackVibrationProvider hapticVibrationProvider) { 1457 if (hapticVibrationProvider == null) { 1458 Slog.e(TAG, reason + "; haptic vibration provider not ready."); 1459 return Status.IGNORED_ERROR_SCHEDULING; 1460 } 1461 if (hapticVibrationProvider.isRestrictedHapticFeedback(constant) 1462 && !hasPermission(android.Manifest.permission.VIBRATE_SYSTEM_CONSTANTS)) { 1463 Slog.w(TAG, reason + "; no permission for system constant " + constant); 1464 return Status.IGNORED_MISSING_PERMISSION; 1465 } 1466 return null; 1467 } 1468 1469 /** 1470 * Return true if the vibration has the same token and usage belongs to given usage class. 1471 * 1472 * @param session The ongoing or pending vibration session to be cancelled. 1473 * @param usageFilter The vibration usages to be cancelled, any bitwise combination of 1474 * VibrationAttributes.USAGE_* values. 1475 * @param tokenFilter The binder token to identify the vibration origin. Only vibrations 1476 * started with the same token can be cancelled with it. 1477 */ shouldCancelSession(@ullable VibrationSession session, int usageFilter, @Nullable IBinder tokenFilter)1478 private boolean shouldCancelSession(@Nullable VibrationSession session, int usageFilter, 1479 @Nullable IBinder tokenFilter) { 1480 if (session == null) { 1481 return false; 1482 } 1483 if (session instanceof VendorVibrationSession) { 1484 // Vendor sessions should not be cancelled by Vibrator.cancel API. 1485 return false; 1486 } 1487 if ((tokenFilter != null) && (tokenFilter != session.getCallerToken())) { 1488 // Vibration from a different app, this should not cancel it. 1489 return false; 1490 } 1491 int usage = session.getCallerInfo().attrs.getUsage(); 1492 if (usage == VibrationAttributes.USAGE_UNKNOWN) { 1493 // Special case, usage UNKNOWN would match all filters. Instead it should only match if 1494 // it's cancelling that usage specifically, or if cancelling all usages. 1495 return usageFilter == VibrationAttributes.USAGE_UNKNOWN 1496 || usageFilter == VibrationAttributes.USAGE_FILTER_MATCH_ALL; 1497 } 1498 return (usageFilter & usage) == usage; 1499 } 1500 1501 /** 1502 * Check which mode should be set for a vibration with given {@code uid}, {@code opPkg} and 1503 * {@code attrs}. This will return one of the AppOpsManager.MODE_*. 1504 */ 1505 @GuardedBy("mLock") checkAppOpModeLocked(CallerInfo callerInfo)1506 private int checkAppOpModeLocked(CallerInfo callerInfo) { 1507 int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE, 1508 callerInfo.attrs.getAudioUsage(), callerInfo.uid, callerInfo.opPkg); 1509 if (DEBUG) { 1510 int opMode = mAppOps.checkOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, 1511 callerInfo.opPkg); 1512 Slog.d(TAG, "Check AppOp mode VIBRATE for uid " + callerInfo.uid + " and package " 1513 + callerInfo.opPkg + " returned audio=" + AppOpsManager.MODE_NAMES[mode] 1514 + ", op=" + AppOpsManager.MODE_NAMES[opMode]); 1515 } 1516 int fixedMode = fixupAppOpModeLocked(mode, callerInfo.attrs); 1517 if (mode != fixedMode && fixedMode == AppOpsManager.MODE_ALLOWED) { 1518 // If we're just ignoring the vibration op then this is set by DND and we should ignore 1519 // if we're asked to bypass. AppOps won't be able to record this operation, so make 1520 // sure we at least note it in the logs for debugging. 1521 Slog.d(TAG, "Bypassing DND for vibrate from uid " + callerInfo.uid); 1522 } 1523 return fixedMode; 1524 } 1525 1526 /** Start an operation in {@link AppOpsManager}, if allowed. */ 1527 @GuardedBy("mLock") startAppOpModeLocked(CallerInfo callerInfo)1528 private int startAppOpModeLocked(CallerInfo callerInfo) { 1529 int mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, callerInfo.uid, 1530 callerInfo.opPkg); 1531 if (DEBUG) { 1532 Slog.d(TAG, "Start AppOp mode VIBRATE for uid " + callerInfo.uid + " and package " 1533 + callerInfo.opPkg + " returned " + AppOpsManager.MODE_NAMES[mode]); 1534 } 1535 return fixupAppOpModeLocked(mode, callerInfo.attrs); 1536 } 1537 1538 /** 1539 * Finish a previously started operation in {@link AppOpsManager}. This will be a noop if no 1540 * operation with same uid was previously started. 1541 */ 1542 @GuardedBy("mLock") finishAppOpModeLocked(CallerInfo callerInfo)1543 private void finishAppOpModeLocked(CallerInfo callerInfo) { 1544 if (DEBUG) { 1545 Slog.d(TAG, "Finish AppOp mode VIBRATE for uid " + callerInfo.uid + " and package " 1546 + callerInfo.opPkg); 1547 } 1548 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, callerInfo.uid, callerInfo.opPkg); 1549 } 1550 1551 /** 1552 * Enforces {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} to incoming UID if it's 1553 * different from the calling UID. 1554 */ enforceUpdateAppOpsStatsPermission(int uid)1555 private void enforceUpdateAppOpsStatsPermission(int uid) { 1556 if (uid == Binder.getCallingUid()) { 1557 return; 1558 } 1559 if (Binder.getCallingPid() == Process.myPid()) { 1560 return; 1561 } 1562 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, 1563 Binder.getCallingPid(), Binder.getCallingUid(), null); 1564 } 1565 1566 /** 1567 * Validate the incoming {@link CombinedVibration}. 1568 * 1569 * We can't throw exceptions here since we might be called from some system_server component, 1570 * which would bring the whole system down. 1571 * 1572 * @return whether the CombinedVibrationEffect is non-null and valid 1573 */ isEffectValid(@ullable CombinedVibration effect)1574 private static boolean isEffectValid(@Nullable CombinedVibration effect) { 1575 if (effect == null) { 1576 Slog.wtf(TAG, "effect must not be null"); 1577 return false; 1578 } 1579 try { 1580 effect.validate(); 1581 } catch (Exception e) { 1582 Slog.wtf(TAG, "Encountered issue when verifying vibration: " + effect, e); 1583 return false; 1584 } 1585 return true; 1586 } 1587 1588 /** 1589 * Return new {@link VibrationAttributes} that only applies flags that this user has permissions 1590 * to use. 1591 */ 1592 @NonNull fixupVibrationAttributes(@ullable VibrationAttributes attrs, @Nullable CombinedVibration effect)1593 private VibrationAttributes fixupVibrationAttributes(@Nullable VibrationAttributes attrs, 1594 @Nullable CombinedVibration effect) { 1595 if (attrs == null) { 1596 attrs = DEFAULT_ATTRIBUTES; 1597 } 1598 int usage = attrs.getUsage(); 1599 if ((usage == VibrationAttributes.USAGE_UNKNOWN) 1600 && (effect != null) && effect.isHapticFeedbackCandidate()) { 1601 usage = VibrationAttributes.USAGE_TOUCH; 1602 } 1603 int flags = attrs.getFlags(); 1604 if ((flags & ATTRIBUTES_ALL_BYPASS_FLAGS) != 0) { 1605 if (!(hasPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) 1606 || hasPermission(android.Manifest.permission.MODIFY_PHONE_STATE) 1607 || hasPermission(android.Manifest.permission.MODIFY_AUDIO_ROUTING))) { 1608 // Remove bypass flags from attributes if the app does not have permissions. 1609 flags &= ~ATTRIBUTES_ALL_BYPASS_FLAGS; 1610 } 1611 } 1612 if ((usage == attrs.getUsage()) && (flags == attrs.getFlags())) { 1613 return attrs; 1614 } 1615 return new VibrationAttributes.Builder(attrs) 1616 .setUsage(usage) 1617 .setFlags(flags, attrs.getFlags()) 1618 .build(); 1619 } 1620 1621 @GuardedBy("mLock") 1622 @Nullable fixupAlwaysOnEffectsLocked(CombinedVibration effect)1623 private SparseArray<PrebakedSegment> fixupAlwaysOnEffectsLocked(CombinedVibration effect) { 1624 SparseArray<VibrationEffect> effects; 1625 if (effect instanceof CombinedVibration.Mono) { 1626 VibrationEffect syncedEffect = ((CombinedVibration.Mono) effect).getEffect(); 1627 effects = transformAllVibratorsLocked(unused -> syncedEffect); 1628 } else if (effect instanceof CombinedVibration.Stereo) { 1629 effects = ((CombinedVibration.Stereo) effect).getEffects(); 1630 } else { 1631 // Only synced combinations can be used for always-on effects. 1632 return null; 1633 } 1634 SparseArray<PrebakedSegment> result = new SparseArray<>(); 1635 for (int i = 0; i < effects.size(); i++) { 1636 PrebakedSegment prebaked = extractPrebakedSegment(effects.valueAt(i)); 1637 if (prebaked == null) { 1638 Slog.e(TAG, "Only prebaked effects supported for always-on."); 1639 return null; 1640 } 1641 int vibratorId = effects.keyAt(i); 1642 VibratorController vibrator = mVibrators.get(vibratorId); 1643 if (vibrator != null && vibrator.hasCapability(IVibrator.CAP_ALWAYS_ON_CONTROL)) { 1644 result.put(vibratorId, prebaked); 1645 } 1646 } 1647 if (result.size() == 0) { 1648 return null; 1649 } 1650 return result; 1651 } 1652 1653 @Nullable extractPrebakedSegment(VibrationEffect effect)1654 private static PrebakedSegment extractPrebakedSegment(VibrationEffect effect) { 1655 if (effect instanceof VibrationEffect.Composed composed) { 1656 if (composed.getSegments().size() == 1) { 1657 VibrationEffectSegment segment = composed.getSegments().get(0); 1658 if (segment instanceof PrebakedSegment prebaked) { 1659 return prebaked; 1660 } 1661 } 1662 } 1663 return null; 1664 } 1665 1666 /** 1667 * Check given mode, one of the AppOpsManager.MODE_*, against {@link VibrationAttributes} to 1668 * allow bypassing {@link AppOpsManager} checks. 1669 */ 1670 @GuardedBy("mLock") fixupAppOpModeLocked(int mode, VibrationAttributes attrs)1671 private int fixupAppOpModeLocked(int mode, VibrationAttributes attrs) { 1672 if (mode == AppOpsManager.MODE_IGNORED 1673 && attrs.isFlagSet(VibrationAttributes.FLAG_BYPASS_INTERRUPTION_POLICY)) { 1674 return AppOpsManager.MODE_ALLOWED; 1675 } 1676 return mode; 1677 } 1678 hasPermission(String permission)1679 private boolean hasPermission(String permission) { 1680 return mContext.checkCallingOrSelfPermission(permission) 1681 == PackageManager.PERMISSION_GRANTED; 1682 } 1683 1684 @GuardedBy("mLock") shouldCancelOnScreenOffLocked(@ullable VibrationSession session)1685 private boolean shouldCancelOnScreenOffLocked(@Nullable VibrationSession session) { 1686 if (session == null) { 1687 return false; 1688 } 1689 return mVibrationSettings.shouldCancelVibrationOnScreenOff(session.getCallerInfo(), 1690 session.getCreateUptimeMillis()); 1691 } 1692 1693 @GuardedBy("mLock") shouldCancelAppOpModeChangedLocked(@ullable VibrationSession session)1694 private boolean shouldCancelAppOpModeChangedLocked(@Nullable VibrationSession session) { 1695 if (session == null) { 1696 return false; 1697 } 1698 return checkAppOpModeLocked(session.getCallerInfo()) != AppOpsManager.MODE_ALLOWED; 1699 } 1700 shouldCancelOnFgUserRequest(@ullable VibrationSession session)1701 private boolean shouldCancelOnFgUserRequest(@Nullable VibrationSession session) { 1702 if (session == null) { 1703 return false; 1704 } 1705 return session.getCallerInfo().attrs.getUsageClass() == USAGE_CLASS_ALARM; 1706 } 1707 1708 @GuardedBy("mLock") onAllVibratorsLocked(Consumer<VibratorController> consumer)1709 private void onAllVibratorsLocked(Consumer<VibratorController> consumer) { 1710 for (int i = 0; i < mVibrators.size(); i++) { 1711 consumer.accept(mVibrators.valueAt(i)); 1712 } 1713 } 1714 1715 @GuardedBy("mLock") transformAllVibratorsLocked(Function<VibratorController, T> fn)1716 private <T> SparseArray<T> transformAllVibratorsLocked(Function<VibratorController, T> fn) { 1717 SparseArray<T> ret = new SparseArray<>(mVibrators.size()); 1718 for (int i = 0; i < mVibrators.size(); i++) { 1719 ret.put(mVibrators.keyAt(i), fn.apply(mVibrators.valueAt(i))); 1720 } 1721 return ret; 1722 } 1723 1724 /** Point of injection for test dependencies */ 1725 @VisibleForTesting 1726 static class Injector { 1727 getNativeWrapper()1728 NativeWrapper getNativeWrapper() { 1729 return new NativeWrapper(); 1730 } 1731 createHandler(Looper looper)1732 Handler createHandler(Looper looper) { 1733 return new Handler(looper); 1734 } 1735 getBatteryStatsService()1736 IBatteryStats getBatteryStatsService() { 1737 return IBatteryStats.Stub.asInterface(ServiceManager.getService( 1738 BatteryStats.SERVICE_NAME)); 1739 } 1740 getFrameworkStatsLogger(Handler handler)1741 VibratorFrameworkStatsLogger getFrameworkStatsLogger(Handler handler) { 1742 return new VibratorFrameworkStatsLogger(handler); 1743 } 1744 createVibratorController(int vibratorId, VibratorController.OnVibrationCompleteListener listener)1745 VibratorController createVibratorController(int vibratorId, 1746 VibratorController.OnVibrationCompleteListener listener) { 1747 return new VibratorController(vibratorId, listener); 1748 } 1749 createHapticFeedbackVibrationProvider( Resources resources, VibratorInfo vibratorInfo)1750 HapticFeedbackVibrationProvider createHapticFeedbackVibrationProvider( 1751 Resources resources, VibratorInfo vibratorInfo) { 1752 return new HapticFeedbackVibrationProvider(resources, vibratorInfo); 1753 } 1754 addService(String name, IBinder service)1755 void addService(String name, IBinder service) { 1756 ServiceManager.addService(name, service); 1757 } 1758 createVibratorControllerHolder()1759 VibratorControllerHolder createVibratorControllerHolder() { 1760 return new VibratorControllerHolder(); 1761 } 1762 isServiceDeclared(String name)1763 boolean isServiceDeclared(String name) { 1764 return ServiceManager.isDeclared(name); 1765 } 1766 } 1767 1768 /** 1769 * Implementation of {@link VibrationThread.VibratorManagerHooks} that controls synced 1770 * vibrations and reports them when finished. 1771 */ 1772 private final class VibrationThreadCallbacks implements VibrationThread.VibratorManagerHooks { 1773 1774 @Override prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds)1775 public boolean prepareSyncedVibration(long requiredCapabilities, int[] vibratorIds) { 1776 Trace.traceBegin(TRACE_TAG_VIBRATOR, "prepareSyncedVibration"); 1777 try { 1778 if ((mCapabilities & requiredCapabilities) != requiredCapabilities) { 1779 // This sync step requires capabilities this device doesn't have, skipping 1780 // sync... 1781 return false; 1782 } 1783 return mNativeWrapper.prepareSynced(vibratorIds); 1784 } finally { 1785 Trace.traceEnd(TRACE_TAG_VIBRATOR); 1786 } 1787 } 1788 1789 @Override triggerSyncedVibration(long vibrationId)1790 public boolean triggerSyncedVibration(long vibrationId) { 1791 Trace.traceBegin(TRACE_TAG_VIBRATOR, "triggerSyncedVibration"); 1792 try { 1793 return mNativeWrapper.triggerSynced(vibrationId); 1794 } finally { 1795 Trace.traceEnd(TRACE_TAG_VIBRATOR); 1796 } 1797 } 1798 1799 @Override cancelSyncedVibration()1800 public void cancelSyncedVibration() { 1801 Trace.traceBegin(TRACE_TAG_VIBRATOR, "cancelSyncedVibration"); 1802 try { 1803 mNativeWrapper.cancelSynced(); 1804 } finally { 1805 Trace.traceEnd(TRACE_TAG_VIBRATOR); 1806 } 1807 } 1808 1809 @Override noteVibratorOn(int uid, long duration)1810 public void noteVibratorOn(int uid, long duration) { 1811 Trace.traceBegin(TRACE_TAG_VIBRATOR, "noteVibratorOn"); 1812 try { 1813 if (duration <= 0) { 1814 // Tried to turn vibrator ON and got: 1815 // duration == 0: Unsupported effect/method or zero-amplitude segment. 1816 // duration < 0: Unexpected error triggering the vibrator. 1817 // Skip battery stats and atom metric for VibratorStageChanged to ON. 1818 return; 1819 } 1820 if (duration == Long.MAX_VALUE) { 1821 // Repeating duration has started. Report a fixed duration here, noteVibratorOff 1822 // should be called when this is cancelled. 1823 duration = BATTERY_STATS_REPEATING_VIBRATION_DURATION; 1824 } 1825 mBatteryStatsService.noteVibratorOn(uid, duration); 1826 mFrameworkStatsLogger.writeVibratorStateOnAsync(uid, duration); 1827 } catch (RemoteException e) { 1828 Slog.e(TAG, "Error logging VibratorStateChanged to ON", e); 1829 } finally { 1830 Trace.traceEnd(TRACE_TAG_VIBRATOR); 1831 } 1832 } 1833 1834 @Override noteVibratorOff(int uid)1835 public void noteVibratorOff(int uid) { 1836 Trace.traceBegin(TRACE_TAG_VIBRATOR, "noteVibratorOff"); 1837 try { 1838 mBatteryStatsService.noteVibratorOff(uid); 1839 mFrameworkStatsLogger.writeVibratorStateOffAsync(uid); 1840 } catch (RemoteException e) { 1841 Slog.e(TAG, "Error logging VibratorStateChanged to OFF", e); 1842 } finally { 1843 Trace.traceEnd(TRACE_TAG_VIBRATOR); 1844 } 1845 } 1846 1847 @Override onVibrationThreadReleased(long vibrationId)1848 public void onVibrationThreadReleased(long vibrationId) { 1849 if (DEBUG) { 1850 Slog.d(TAG, "VibrationThread released vibration " + vibrationId); 1851 } 1852 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onVibrationThreadReleased"); 1853 try { 1854 synchronized (mLock) { 1855 if (mCurrentSession instanceof SingleVibrationSession session) { 1856 if (Build.IS_DEBUGGABLE && (session.getVibration().id != vibrationId)) { 1857 Slog.wtf(TAG, TextUtils.formatSimple( 1858 "VibrationId mismatch on vibration thread release." 1859 + " expected=%d, released=%d", 1860 session.getVibration().id, vibrationId)); 1861 } 1862 finishAppOpModeLocked(mCurrentSession.getCallerInfo()); 1863 clearCurrentSessionLocked(); 1864 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); 1865 // Start next vibration if it's waiting for the thread. 1866 maybeStartNextSessionLocked(); 1867 } else if (mCurrentSession instanceof VendorVibrationSession session) { 1868 VibrationStepConductor conductor = session.clearVibrationConductor(); 1869 if (Build.IS_DEBUGGABLE) { 1870 if (conductor == null) { 1871 Slog.wtf(TAG, "Vendor session without ongoing vibration on" 1872 + " thread release. currentSession=" + mCurrentSession); 1873 } else if (conductor.getVibration().id != vibrationId) { 1874 Slog.wtf(TAG, TextUtils.formatSimple( 1875 "VibrationId mismatch on vibration thread release." 1876 + " expected=%d, released=%d", 1877 conductor.getVibration().id, vibrationId)); 1878 } 1879 } 1880 } else if (Build.IS_DEBUGGABLE) { 1881 Slog.wtf(TAG, "VibrationSession invalid on vibration thread release." 1882 + " currentSession=" + mCurrentSession); 1883 } 1884 } 1885 } finally { 1886 Trace.traceEnd(TRACE_TAG_VIBRATOR); 1887 } 1888 } 1889 } 1890 1891 /** 1892 * Implementation of {@link ExternalVibrationSession.VibratorManagerHooks} that controls 1893 * external vibrations and reports them when finished. 1894 */ 1895 private final class ExternalVibrationCallbacks 1896 implements ExternalVibrationSession.VibratorManagerHooks { 1897 1898 @Override onExternalVibrationReleased(long vibrationId)1899 public void onExternalVibrationReleased(long vibrationId) { 1900 if (DEBUG) { 1901 Slog.d(TAG, "External vibration " + vibrationId + " released"); 1902 } 1903 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onExternalVibrationReleased"); 1904 try { 1905 synchronized (mLock) { 1906 if (!(mCurrentSession instanceof ExternalVibrationSession session)) { 1907 if (Build.IS_DEBUGGABLE) { 1908 Slog.wtf(TAG, "VibrationSession invalid on external vibration release." 1909 + " currentSession=" + mCurrentSession); 1910 } 1911 // Only external vibration sessions are ended by this callback. Abort. 1912 return; 1913 } 1914 if (Build.IS_DEBUGGABLE && (session.id != vibrationId)) { 1915 Slog.wtf(TAG, TextUtils.formatSimple( 1916 "VibrationId mismatch on external vibration release." 1917 + " expected=%d, released=%d", session.id, vibrationId)); 1918 } 1919 setExternalControl(false, session.stats); 1920 clearCurrentSessionLocked(); 1921 // Start next vibration if it's waiting for the external control to be over. 1922 maybeStartNextSessionLocked(); 1923 } 1924 } finally { 1925 Trace.traceEnd(TRACE_TAG_VIBRATOR); 1926 } 1927 } 1928 } 1929 1930 /** 1931 * Implementation of {@link ExternalVibrationSession.VibratorManagerHooks} that controls 1932 * external vibrations and reports them when finished. 1933 */ 1934 private final class VendorVibrationSessionCallbacks 1935 implements VendorVibrationSession.VibratorManagerHooks { 1936 1937 @Override vibrate(long sessionId, CallerInfo callerInfo, CombinedVibration effect)1938 public void vibrate(long sessionId, CallerInfo callerInfo, CombinedVibration effect) { 1939 if (DEBUG) { 1940 Slog.d(TAG, "Vibration session " + sessionId + " vibration requested"); 1941 } 1942 Trace.traceBegin(TRACE_TAG_VIBRATOR, "sessionVibrate"); 1943 try { 1944 synchronized (mLock) { 1945 if (!(mCurrentSession instanceof VendorVibrationSession session)) { 1946 if (Build.IS_DEBUGGABLE) { 1947 Slog.wtf(TAG, "VibrationSession invalid on session vibrate." 1948 + " currentSession=" + mCurrentSession); 1949 } 1950 // Only vendor vibration sessions can handle this call. Abort. 1951 return; 1952 } 1953 if (session.getSessionId() != sessionId) { 1954 if (Build.IS_DEBUGGABLE) { 1955 Slog.wtf(TAG, TextUtils.formatSimple( 1956 "SessionId mismatch on vendor vibration session vibrate." 1957 + " expected=%d, released=%d", 1958 session.getSessionId(), sessionId)); 1959 } 1960 // Only the ongoing vendor vibration sessions can handle this call. Abort. 1961 return; 1962 } 1963 if (session.wasEndRequested()) { 1964 if (DEBUG) { 1965 Slog.d(TAG, "session vibrate; session is ending, vibration ignored"); 1966 } 1967 session.notifyVibrationAttempt(createVibrationAttemptDebugInfo(effect, 1968 callerInfo, Status.IGNORED_ERROR_SCHEDULING)); 1969 return; 1970 } 1971 if (!isEffectValid(effect)) { 1972 session.notifyVibrationAttempt(createVibrationAttemptDebugInfo(effect, 1973 callerInfo, Status.IGNORED_UNSUPPORTED)); 1974 return; 1975 } 1976 if (effect.getDuration() == Long.MAX_VALUE) { 1977 // Repeating effects cannot be played by the service in a session. 1978 session.notifyVibrationAttempt(createVibrationAttemptDebugInfo(effect, 1979 callerInfo, Status.IGNORED_UNSUPPORTED)); 1980 return; 1981 } 1982 // Create Vibration.Stats as close to the request as possible, for tracking. 1983 HalVibration vib = new HalVibration(callerInfo, effect); 1984 vib.fillFallbacks(mVibrationSettings::getFallbackEffect); 1985 1986 if (callerInfo.attrs.isFlagSet( 1987 VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) { 1988 // Force update of user settings before checking if this vibration effect 1989 // should be ignored or scaled. 1990 mVibrationSettings.update(); 1991 } 1992 1993 if (DEBUG) { 1994 Slog.d(TAG, "Starting vibrate for vibration " + vib.id 1995 + " in session " + sessionId); 1996 } 1997 1998 VibrationStepConductor conductor = 1999 createSessionVibrationStepConductor(vib, session.getDeviceAdapter()); 2000 if (session.maybeSetVibrationConductor(conductor)) { 2001 if (!mVibrationThread.runVibrationOnVibrationThread(conductor)) { 2002 // Shouldn't happen. The method call already logs. 2003 vib.end(new Vibration.EndInfo(Status.IGNORED_ERROR_SCHEDULING)); 2004 session.clearVibrationConductor(); // Rejected by thread, clear it. 2005 } 2006 } else { 2007 // Cannot set vibration in session, log failed attempt. 2008 session.notifyVibrationAttempt(createVibrationAttemptDebugInfo(effect, 2009 callerInfo, Status.IGNORED_ERROR_SCHEDULING)); 2010 } 2011 } 2012 } finally { 2013 Trace.traceEnd(TRACE_TAG_VIBRATOR); 2014 } 2015 } 2016 2017 @Override endSession(long sessionId, boolean shouldAbort)2018 public void endSession(long sessionId, boolean shouldAbort) { 2019 if (DEBUG) { 2020 Slog.d(TAG, "Vibration session " + sessionId 2021 + (shouldAbort ? " aborting" : " ending")); 2022 } 2023 Trace.traceBegin(TRACE_TAG_VIBRATOR, "endSession"); 2024 try { 2025 mNativeWrapper.endSession(sessionId, shouldAbort); 2026 } finally { 2027 Trace.traceEnd(TRACE_TAG_VIBRATOR); 2028 } 2029 } 2030 2031 @Override onSessionReleased(long sessionId)2032 public void onSessionReleased(long sessionId) { 2033 if (DEBUG) { 2034 Slog.d(TAG, "Vibration session " + sessionId + " released"); 2035 } 2036 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onVendorSessionReleased"); 2037 try { 2038 synchronized (mLock) { 2039 if (!(mCurrentSession instanceof VendorVibrationSession session)) { 2040 if (Build.IS_DEBUGGABLE) { 2041 Slog.wtf(TAG, "VibrationSession invalid on vibration session release." 2042 + " currentSession=" + mCurrentSession); 2043 } 2044 // Only vendor vibration sessions are ended by this callback. Abort. 2045 return; 2046 } 2047 if (Build.IS_DEBUGGABLE && (session.getSessionId() != sessionId)) { 2048 Slog.wtf(TAG, TextUtils.formatSimple( 2049 "SessionId mismatch on vendor vibration session release." 2050 + " expected=%d, released=%d", 2051 session.getSessionId(), sessionId)); 2052 } 2053 // Make sure all controllers in session are reset after session ended. 2054 // This will update the vibrator state to isVibrating = false for listeners. 2055 for (int vibratorId : session.getVibratorIds()) { 2056 mVibrators.get(vibratorId).off(); 2057 } 2058 finishAppOpModeLocked(mCurrentSession.getCallerInfo()); 2059 clearCurrentSessionLocked(); 2060 // Start next vibration if it's waiting for the HAL session to be over. 2061 maybeStartNextSessionLocked(); 2062 } 2063 } finally { 2064 Trace.traceEnd(TRACE_TAG_VIBRATOR); 2065 } 2066 } 2067 } 2068 2069 /** Listener for vibrator manager completion callbacks from native. */ 2070 @VisibleForTesting 2071 interface VibratorManagerNativeCallbacks { 2072 2073 /** Callback triggered when synced vibration is complete. */ onSyncedVibrationComplete(long vibrationId)2074 void onSyncedVibrationComplete(long vibrationId); 2075 2076 /** Callback triggered when vibration session is complete. */ onVibrationSessionComplete(long sessionId)2077 void onVibrationSessionComplete(long sessionId); 2078 } 2079 2080 /** 2081 * Implementation of listeners to native vibrators with a weak reference to this service. 2082 */ 2083 private static final class VibrationCompleteListener implements 2084 VibratorController.OnVibrationCompleteListener, VibratorManagerNativeCallbacks { 2085 private WeakReference<VibratorManagerService> mServiceRef; 2086 VibrationCompleteListener(VibratorManagerService service)2087 VibrationCompleteListener(VibratorManagerService service) { 2088 mServiceRef = new WeakReference<>(service); 2089 } 2090 2091 @Override onSyncedVibrationComplete(long vibrationId)2092 public void onSyncedVibrationComplete(long vibrationId) { 2093 VibratorManagerService service = mServiceRef.get(); 2094 if (service != null) { 2095 service.onSyncedVibrationComplete(vibrationId); 2096 } 2097 } 2098 2099 @Override onVibrationSessionComplete(long sessionId)2100 public void onVibrationSessionComplete(long sessionId) { 2101 VibratorManagerService service = mServiceRef.get(); 2102 if (service != null) { 2103 service.onVibrationSessionComplete(sessionId); 2104 } 2105 } 2106 2107 @Override onComplete(int vibratorId, long vibrationId, long stepId)2108 public void onComplete(int vibratorId, long vibrationId, long stepId) { 2109 VibratorManagerService service = mServiceRef.get(); 2110 if (service != null) { 2111 service.onVibrationComplete(vibratorId, vibrationId, stepId); 2112 } 2113 } 2114 } 2115 2116 /** 2117 * Combination of prekabed vibrations on multiple vibrators, with the same {@link 2118 * VibrationAttributes}, that can be set for always-on effects. 2119 */ 2120 private static final class AlwaysOnVibration { 2121 public final int alwaysOnId; 2122 public final CallerInfo callerInfo; 2123 public final SparseArray<PrebakedSegment> effects; 2124 AlwaysOnVibration(int alwaysOnId, CallerInfo callerInfo, SparseArray<PrebakedSegment> effects)2125 AlwaysOnVibration(int alwaysOnId, CallerInfo callerInfo, 2126 SparseArray<PrebakedSegment> effects) { 2127 this.alwaysOnId = alwaysOnId; 2128 this.callerInfo = callerInfo; 2129 this.effects = effects; 2130 } 2131 } 2132 2133 /** Wrapper around the static-native methods of {@link VibratorManagerService} for tests. */ 2134 @VisibleForTesting 2135 public static class NativeWrapper { 2136 2137 private long mNativeServicePtr = 0; 2138 2139 /** Returns native pointer to newly created controller and connects with HAL service. */ init(VibratorManagerNativeCallbacks listener)2140 public void init(VibratorManagerNativeCallbacks listener) { 2141 mNativeServicePtr = nativeInit(listener); 2142 long finalizerPtr = nativeGetFinalizer(); 2143 2144 if (finalizerPtr != 0) { 2145 NativeAllocationRegistry registry = 2146 NativeAllocationRegistry.createMalloced( 2147 VibratorManagerService.class.getClassLoader(), finalizerPtr); 2148 registry.registerNativeAllocation(this, mNativeServicePtr); 2149 } 2150 } 2151 2152 /** Returns manager capabilities. */ getCapabilities()2153 public long getCapabilities() { 2154 return nativeGetCapabilities(mNativeServicePtr); 2155 } 2156 2157 /** Returns vibrator ids. */ getVibratorIds()2158 public int[] getVibratorIds() { 2159 return nativeGetVibratorIds(mNativeServicePtr); 2160 } 2161 2162 /** Prepare vibrators for triggering vibrations in sync. */ prepareSynced(@onNull int[] vibratorIds)2163 public boolean prepareSynced(@NonNull int[] vibratorIds) { 2164 return nativePrepareSynced(mNativeServicePtr, vibratorIds); 2165 } 2166 2167 /** Trigger prepared synced vibration. */ triggerSynced(long vibrationId)2168 public boolean triggerSynced(long vibrationId) { 2169 return nativeTriggerSynced(mNativeServicePtr, vibrationId); 2170 } 2171 2172 /** Cancel prepared synced vibration. */ cancelSynced()2173 public void cancelSynced() { 2174 nativeCancelSynced(mNativeServicePtr); 2175 } 2176 2177 /** Start vibration session. */ startSession(long sessionId, @NonNull int[] vibratorIds)2178 public boolean startSession(long sessionId, @NonNull int[] vibratorIds) { 2179 return nativeStartSession(mNativeServicePtr, sessionId, vibratorIds); 2180 } 2181 2182 /** End vibration session. */ endSession(long sessionId, boolean shouldAbort)2183 public void endSession(long sessionId, boolean shouldAbort) { 2184 nativeEndSession(mNativeServicePtr, sessionId, shouldAbort); 2185 } 2186 2187 /** Clear vibration sessions. */ clearSessions()2188 public void clearSessions() { 2189 nativeClearSessions(mNativeServicePtr); 2190 } 2191 } 2192 2193 /** Keep records of vibrations played and provide debug information for this service. */ 2194 private static final class VibratorManagerRecords { 2195 private final VibrationRecords mAggregatedVibrationHistory; 2196 private final VibrationRecords mRecentVibrations; 2197 VibratorManagerRecords(int recentVibrationSizeLimit, int aggregationSizeLimit, int aggregationTimeLimit)2198 VibratorManagerRecords(int recentVibrationSizeLimit, int aggregationSizeLimit, 2199 int aggregationTimeLimit) { 2200 mAggregatedVibrationHistory = 2201 new VibrationRecords(aggregationSizeLimit, aggregationTimeLimit); 2202 // Recent vibrations are not aggregated, to help debugging issues that just happened. 2203 mRecentVibrations = 2204 new VibrationRecords(recentVibrationSizeLimit, /* aggregationTimeLimit= */ 0); 2205 } 2206 record(DebugInfo info)2207 synchronized void record(DebugInfo info) { 2208 GroupedAggregatedLogRecords.AggregatedLogRecord<VibrationRecord> droppedRecord = 2209 mRecentVibrations.add(new VibrationRecord(info)); 2210 if (droppedRecord != null) { 2211 // Move dropped record from recent list to aggregated history list. 2212 mAggregatedVibrationHistory.add(droppedRecord.getLatest()); 2213 } 2214 } 2215 dump(IndentingPrintWriter pw)2216 synchronized void dump(IndentingPrintWriter pw) { 2217 pw.println("Recent vibrations:"); 2218 pw.increaseIndent(); 2219 mRecentVibrations.dump(pw); 2220 pw.decreaseIndent(); 2221 2222 pw.println(); 2223 pw.println(); 2224 pw.println("Aggregated vibration history:"); 2225 pw.increaseIndent(); 2226 mAggregatedVibrationHistory.dump(pw); 2227 pw.decreaseIndent(); 2228 } 2229 dump(ProtoOutputStream proto)2230 synchronized void dump(ProtoOutputStream proto) { 2231 mRecentVibrations.dump(proto); 2232 } 2233 } 2234 2235 /** Keep records of vibrations played and provide debug information for this service. */ 2236 private static final class VibrationRecords 2237 extends GroupedAggregatedLogRecords<VibrationRecord> { 2238 VibrationRecords(int sizeLimit, int aggregationTimeLimit)2239 VibrationRecords(int sizeLimit, int aggregationTimeLimit) { 2240 super(sizeLimit, aggregationTimeLimit); 2241 } 2242 2243 @Override dumpGroupHeader(IndentingPrintWriter pw, int usage)2244 void dumpGroupHeader(IndentingPrintWriter pw, int usage) { 2245 pw.println(VibrationAttributes.usageToString(usage) + ":"); 2246 } 2247 2248 @Override findGroupKeyProtoFieldId(int usage)2249 long findGroupKeyProtoFieldId(int usage) { 2250 return switch (usage) { 2251 case VibrationAttributes.USAGE_RINGTONE -> 2252 VibratorManagerServiceDumpProto.PREVIOUS_RING_VIBRATIONS; 2253 case VibrationAttributes.USAGE_NOTIFICATION -> 2254 VibratorManagerServiceDumpProto.PREVIOUS_NOTIFICATION_VIBRATIONS; 2255 case VibrationAttributes.USAGE_ALARM -> 2256 VibratorManagerServiceDumpProto.PREVIOUS_ALARM_VIBRATIONS; 2257 default -> 2258 VibratorManagerServiceDumpProto.PREVIOUS_VIBRATIONS; 2259 }; 2260 } 2261 } 2262 2263 /** 2264 * Record for a single {@link DebugInfo}, that can be grouped by usage and aggregated by UID, 2265 * {@link VibrationAttributes} and {@link CombinedVibration}. 2266 */ 2267 private static final class VibrationRecord 2268 implements GroupedAggregatedLogRecords.SingleLogRecord { 2269 private final DebugInfo mInfo; 2270 VibrationRecord(DebugInfo info)2271 VibrationRecord(DebugInfo info) { 2272 mInfo = info; 2273 } 2274 2275 @Override getGroupKey()2276 public int getGroupKey() { 2277 return mInfo.getCallerInfo().attrs.getUsage(); 2278 } 2279 2280 @Override getCreateUptimeMs()2281 public long getCreateUptimeMs() { 2282 return mInfo.getCreateUptimeMillis(); 2283 } 2284 2285 @Override mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record)2286 public boolean mayAggregate(GroupedAggregatedLogRecords.SingleLogRecord record) { 2287 if (!(record instanceof VibrationRecord)) { 2288 return false; 2289 } 2290 DebugInfo info = ((VibrationRecord) record).mInfo; 2291 return mInfo.getCallerInfo().uid == info.getCallerInfo().uid 2292 && Objects.equals(mInfo.getCallerInfo().attrs, info.getCallerInfo().attrs) 2293 && Objects.equals(mInfo.getDumpAggregationKey(), info.getDumpAggregationKey()); 2294 } 2295 2296 @Override dump(IndentingPrintWriter pw)2297 public void dump(IndentingPrintWriter pw) { 2298 // Prints a compact version of each vibration request for dumpsys. 2299 mInfo.dumpCompact(pw); 2300 } 2301 2302 @Override dump(ProtoOutputStream proto, long fieldId)2303 public void dump(ProtoOutputStream proto, long fieldId) { 2304 mInfo.dump(proto, fieldId); 2305 } 2306 } 2307 2308 /** Clears mNextVibration if set, ending it cleanly */ 2309 @GuardedBy("mLock") clearNextSessionLocked(Status status)2310 private void clearNextSessionLocked(Status status) { 2311 clearNextSessionLocked(status, /* endedBy= */ null); 2312 } 2313 2314 /** Clears mNextVibration if set, ending it cleanly */ 2315 @GuardedBy("mLock") clearNextSessionLocked(Status status, CallerInfo endedBy)2316 private void clearNextSessionLocked(Status status, CallerInfo endedBy) { 2317 if (mNextSession != null) { 2318 if (DEBUG) { 2319 Slog.d(TAG, "Dropping pending vibration from " + mNextSession.getCallerInfo() 2320 + " with status: " + status); 2321 } 2322 // Clearing next vibration before playing it, end it and report metrics right away. 2323 endSessionLocked(mNextSession, status, endedBy); 2324 mNextSession = null; 2325 } 2326 } 2327 2328 /** Clears mCurrentVibration if set, reporting metrics */ 2329 @GuardedBy("mLock") clearCurrentSessionLocked()2330 private void clearCurrentSessionLocked() { 2331 if (mCurrentSession != null) { 2332 mCurrentSession.unlinkToDeath(); 2333 logAndRecordVibration(mCurrentSession.getDebugInfo()); 2334 mCurrentSession = null; 2335 mLock.notify(); // Notify if waiting for current vibration to end. 2336 } 2337 } 2338 2339 @GuardedBy("mLock") maybeClearCurrentAndNextSessionsLocked( Predicate<VibrationSession> shouldEndSessionPredicate, Status endStatus)2340 private void maybeClearCurrentAndNextSessionsLocked( 2341 Predicate<VibrationSession> shouldEndSessionPredicate, Status endStatus) { 2342 if (Flags.fixExternalVibrationSystemUpdateAware()) { 2343 if (shouldEndSessionPredicate.test(mNextSession)) { 2344 clearNextSessionLocked(endStatus); 2345 } 2346 if (shouldEndSessionPredicate.test(mCurrentSession)) { 2347 mCurrentSession.requestEnd(endStatus); 2348 } 2349 } else { 2350 if (!(mNextSession instanceof ExternalVibrationSession) 2351 && shouldEndSessionPredicate.test(mNextSession)) { 2352 clearNextSessionLocked(endStatus); 2353 } 2354 if (!(mCurrentSession instanceof ExternalVibrationSession) 2355 && shouldEndSessionPredicate.test(mCurrentSession)) { 2356 mCurrentSession.requestEnd(endStatus); 2357 } 2358 } 2359 } 2360 2361 /** 2362 * Waits until the current vibration finished processing, timing out after the given 2363 * number of milliseconds. 2364 * 2365 * @return true if the vibration completed, or false if waiting timed out. 2366 */ waitForCurrentSessionEnd(long maxWaitMillis)2367 public boolean waitForCurrentSessionEnd(long maxWaitMillis) { 2368 long now = SystemClock.elapsedRealtime(); 2369 long deadline = now + maxWaitMillis; 2370 synchronized (mLock) { 2371 while (true) { 2372 if (mCurrentSession == null) { 2373 return true; // Done 2374 } 2375 if (now >= deadline) { // Note that thread.wait(0) waits indefinitely. 2376 return false; // Timed out. 2377 } 2378 try { 2379 mLock.wait(deadline - now); 2380 } catch (InterruptedException e) { 2381 Slog.w(TAG, "VibratorManagerService interrupted waiting to stop, continuing"); 2382 } 2383 now = SystemClock.elapsedRealtime(); 2384 } 2385 } 2386 } 2387 getHapticVibrationProvider()2388 private HapticFeedbackVibrationProvider getHapticVibrationProvider() { 2389 synchronized (mLock) { 2390 // Used a cached haptic vibration provider if one exists. 2391 if (mHapticFeedbackVibrationProvider != null) { 2392 return mHapticFeedbackVibrationProvider; 2393 } 2394 VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo(); 2395 if (combinedVibratorInfo == null) { 2396 return null; 2397 } 2398 return mHapticFeedbackVibrationProvider = 2399 mInjector.createHapticFeedbackVibrationProvider( 2400 mContext.getResources(), combinedVibratorInfo); 2401 } 2402 } 2403 2404 @Nullable getCombinedVibratorInfo()2405 private VibratorInfo getCombinedVibratorInfo() { 2406 synchronized (mLock) { 2407 // This is only initialized at system ready, when all vibrator infos are fully loaded. 2408 return mCombinedVibratorInfo; 2409 } 2410 } 2411 2412 /** Implementation of {@link IExternalVibratorService} to be triggered on external control. */ 2413 @VisibleForTesting 2414 final class ExternalVibratorService extends IExternalVibratorService.Stub { 2415 2416 @Override onExternalVibrationStart(ExternalVibration vib)2417 public ExternalVibrationScale onExternalVibrationStart(ExternalVibration vib) { 2418 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onExternalVibrationStart"); 2419 try { 2420 // Create Vibration.Stats as close to the received request as possible, for 2421 // tracking. 2422 ExternalVibrationSession session = new ExternalVibrationSession(vib, 2423 mExternalVibrationCallbacks); 2424 // Mute the request until we run all the checks and accept the vibration. 2425 session.muteScale(); 2426 boolean waitForCompletion = false; 2427 2428 synchronized (mLock) { 2429 if (!hasExternalControlCapability()) { 2430 endSessionLocked(session, Status.IGNORED_UNSUPPORTED); 2431 return session.getScale(); 2432 } 2433 2434 if (ActivityManager.checkComponentPermission( 2435 android.Manifest.permission.VIBRATE, 2436 vib.getUid(), -1 /*owningUid*/, true /*exported*/) 2437 != PackageManager.PERMISSION_GRANTED) { 2438 Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid() 2439 + " tried to play externally controlled vibration" 2440 + " without VIBRATE permission, ignoring."); 2441 endSessionLocked(session, Status.IGNORED_MISSING_PERMISSION); 2442 return session.getScale(); 2443 } 2444 2445 Status ignoreStatus = shouldIgnoreVibrationLocked(session.callerInfo); 2446 if (ignoreStatus != null) { 2447 endSessionLocked(session, ignoreStatus); 2448 return session.getScale(); 2449 } 2450 2451 if ((mCurrentSession instanceof ExternalVibrationSession evs) 2452 && evs.isHoldingSameVibration(vib)) { 2453 // We are already playing this external vibration, so we can return the same 2454 // scale calculated in the previous call to this method. 2455 return evs.getScale(); 2456 } 2457 2458 // Check if ongoing vibration is more important than this vibration. 2459 Vibration.EndInfo ignoreInfo = shouldIgnoreForOngoingLocked(session); 2460 if (ignoreInfo != null) { 2461 endSessionLocked(session, ignoreInfo.status, ignoreInfo.endedBy); 2462 return session.getScale(); 2463 } 2464 2465 // First clear next request, so it won't start when the current one ends. 2466 clearNextSessionLocked(Status.IGNORED_FOR_EXTERNAL, session.callerInfo); 2467 mNextSession = session; 2468 2469 if (mCurrentSession != null) { 2470 // Cancel any vibration that may be playing and ready the vibrator, even if 2471 // we have an externally controlled vibration playing already. 2472 // Since the interface defines that only one externally controlled 2473 // vibration can play at a time, we need to first mute the ongoing vibration 2474 // and then return a scale from this function for the new one, so we can be 2475 // assured that the ongoing will be muted in favor of the new vibration. 2476 // 2477 // Note that this doesn't support multiple concurrent external controls, 2478 // as we would need to mute the old one still if it came from a different 2479 // controller. 2480 session.stats.reportInterruptedAnotherVibration( 2481 mCurrentSession.getCallerInfo()); 2482 mCurrentSession.requestEnd(Status.CANCELLED_SUPERSEDED, 2483 session.callerInfo, /* immediate= */ true); 2484 waitForCompletion = true; 2485 } 2486 } 2487 // Wait for lock and interact with HAL to set external control outside main lock. 2488 if (waitForCompletion) { 2489 if (!waitForCurrentSessionEnd(VIBRATION_CANCEL_WAIT_MILLIS)) { 2490 Slog.e(TAG, "Timed out waiting for vibration to cancel"); 2491 synchronized (mLock) { 2492 if (mNextSession == session) { 2493 mNextSession = null; 2494 } 2495 endSessionLocked(session, Status.IGNORED_ERROR_CANCELLING); 2496 return session.getScale(); 2497 } 2498 } 2499 } 2500 synchronized (mLock) { 2501 if (mNextSession == session) { 2502 // This is still the next vibration to be played. 2503 mNextSession = null; 2504 } else { 2505 // A new request took the place of this one, maybe with higher importance. 2506 // Next vibration already cleared with the right status, just return here. 2507 return session.getScale(); 2508 } 2509 if (!session.linkToDeath()) { 2510 endSessionLocked(session, Status.IGNORED_ERROR_TOKEN); 2511 return session.getScale(); 2512 } 2513 if (DEBUG) { 2514 Slog.d(TAG, "Vibrator going under external control."); 2515 } 2516 setExternalControl(true, session.stats); 2517 if (DEBUG) { 2518 Slog.d(TAG, "Playing external vibration: " + vib); 2519 } 2520 VibrationAttributes attrs = fixupVibrationAttributes( 2521 vib.getVibrationAttributes(), /* effect= */ null); 2522 if (attrs.isFlagSet(VibrationAttributes.FLAG_INVALIDATE_SETTINGS_CACHE)) { 2523 // Force update of user settings before checking if this vibration effect 2524 // should be ignored or scaled. 2525 mVibrationSettings.update(); 2526 } 2527 mCurrentSession = session; 2528 session.scale(mVibrationScaler, attrs.getUsage()); 2529 2530 // Vibrator will start receiving data from external channels after this point. 2531 // Report current time as the vibration start time, for debugging. 2532 session.stats.reportStarted(); 2533 return session.getScale(); 2534 } 2535 } finally { 2536 Trace.traceEnd(TRACE_TAG_VIBRATOR); 2537 } 2538 } 2539 2540 @Override onExternalVibrationStop(ExternalVibration vib)2541 public void onExternalVibrationStop(ExternalVibration vib) { 2542 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onExternalVibrationStop"); 2543 try { 2544 synchronized (mLock) { 2545 if ((mCurrentSession instanceof ExternalVibrationSession evs) 2546 && evs.isHoldingSameVibration(vib)) { 2547 if (DEBUG) { 2548 Slog.d(TAG, "Stopping external vibration: " + vib); 2549 } 2550 mCurrentSession.requestEnd(Status.FINISHED); 2551 } else if (Build.IS_DEBUGGABLE) { 2552 Slog.wtf(TAG, "VibrationSession invalid on external vibration stop." 2553 + " currentSession=" + mCurrentSession + ", received=" + vib); 2554 } 2555 } 2556 } finally { 2557 Trace.traceEnd(TRACE_TAG_VIBRATOR); 2558 } 2559 } 2560 hasExternalControlCapability()2561 private boolean hasExternalControlCapability() { 2562 for (int i = 0; i < mVibrators.size(); i++) { 2563 if (mVibrators.valueAt(i).hasCapability(IVibrator.CAP_EXTERNAL_CONTROL)) { 2564 return true; 2565 } 2566 } 2567 return false; 2568 } 2569 } 2570 2571 /** Provide limited functionality from {@link VibratorManagerService} as shell commands. */ 2572 private final class VibratorManagerShellCommand extends ShellCommand { 2573 public static final String SHELL_PACKAGE_NAME = "com.android.shell"; 2574 public static final long VIBRATION_END_TIMEOUT_MS = 500; // Clean up shouldn't be too long. 2575 2576 private final class CommonOptions { 2577 public boolean force = false; 2578 public String description = "Shell command"; 2579 public boolean background = false; 2580 CommonOptions()2581 CommonOptions() { 2582 String nextArg; 2583 while ((nextArg = peekNextArg()) != null) { 2584 switch (nextArg) { 2585 case "-f": 2586 getNextArgRequired(); // consume "-f" 2587 force = true; 2588 break; 2589 case "-B": 2590 getNextArgRequired(); // consume "-B" 2591 background = true; 2592 break; 2593 case "-d": 2594 getNextArgRequired(); // consume "-d" 2595 description = getNextArgRequired(); 2596 break; 2597 default: 2598 // nextArg is not a common option, finish reading. 2599 return; 2600 } 2601 } 2602 } 2603 } 2604 2605 private final IBinder mShellCallbacksToken; 2606 VibratorManagerShellCommand(IBinder shellCallbacksToken)2607 private VibratorManagerShellCommand(IBinder shellCallbacksToken) { 2608 mShellCallbacksToken = shellCallbacksToken; 2609 } 2610 2611 @Override onCommand(String cmd)2612 public int onCommand(String cmd) { 2613 try { 2614 if ("list".equals(cmd)) { 2615 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: list"); 2616 return runListVibrators(); 2617 } 2618 if ("synced".equals(cmd)) { 2619 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: synced"); 2620 return runMono(); 2621 } 2622 if ("combined".equals(cmd)) { 2623 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: combined"); 2624 return runStereo(); 2625 } 2626 if ("sequential".equals(cmd)) { 2627 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: sequential"); 2628 return runSequential(); 2629 } 2630 if ("xml".equals(cmd)) { 2631 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: xml"); 2632 return runXml(); 2633 } 2634 if ("cancel".equals(cmd)) { 2635 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: cancel"); 2636 return runCancel(); 2637 } 2638 if ("feedback".equals(cmd)) { 2639 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: feedback"); 2640 return runHapticFeedback(); 2641 } 2642 Trace.traceBegin(TRACE_TAG_VIBRATOR, "onCommand: default"); 2643 return handleDefaultCommands(cmd); 2644 } finally { 2645 Trace.traceEnd(TRACE_TAG_VIBRATOR); 2646 } 2647 } 2648 runListVibrators()2649 private int runListVibrators() { 2650 try (PrintWriter pw = getOutPrintWriter();) { 2651 if (mVibratorIds.length == 0) { 2652 pw.println("No vibrator found"); 2653 } else { 2654 for (int id : mVibratorIds) { 2655 pw.println(id); 2656 } 2657 } 2658 pw.println(""); 2659 return 0; 2660 } 2661 } 2662 2663 /** 2664 * Runs a CombinedVibration using the configured common options and attributes. 2665 */ runVibrate(CommonOptions commonOptions, CombinedVibration combined)2666 private void runVibrate(CommonOptions commonOptions, CombinedVibration combined) { 2667 VibrationAttributes attrs = createVibrationAttributes(commonOptions); 2668 // If running in the background, bind to death of the server binder rather than the 2669 // client, and the cancel command likewise uses the server binder reference to 2670 // only cancel background vibrations. 2671 IBinder deathBinder = commonOptions.background ? VibratorManagerService.this 2672 : mShellCallbacksToken; 2673 int uid = Binder.getCallingUid(); 2674 // Resolve the package name for the client based on the process UID, to cover cases like 2675 // rooted shell clients using ROOT_UID. 2676 String resolvedPackageName = AppOpsManager.resolvePackageName(uid, SHELL_PACKAGE_NAME); 2677 HalVibration vib = vibrateWithPermissionCheck(uid, Context.DEVICE_ID_DEFAULT, 2678 resolvedPackageName, combined, attrs, commonOptions.description, deathBinder); 2679 maybeWaitOnVibration(vib, commonOptions); 2680 } 2681 runMono()2682 private int runMono() { 2683 runVibrate(new CommonOptions(), CombinedVibration.createParallel(nextEffect())); 2684 return 0; 2685 } 2686 runStereo()2687 private int runStereo() { 2688 CommonOptions commonOptions = new CommonOptions(); 2689 CombinedVibration.ParallelCombination combination = 2690 CombinedVibration.startParallel(); 2691 while ("-v".equals(getNextOption())) { 2692 int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v"); 2693 combination.addVibrator(vibratorId, nextEffect()); 2694 } 2695 runVibrate(commonOptions, combination.combine()); 2696 return 0; 2697 } 2698 runSequential()2699 private int runSequential() { 2700 CommonOptions commonOptions = new CommonOptions(); 2701 CombinedVibration.SequentialCombination combination = 2702 CombinedVibration.startSequential(); 2703 while ("-v".equals(getNextOption())) { 2704 int vibratorId = parseInt(getNextArgRequired(), "Expected vibrator id after -v"); 2705 combination.addNext(vibratorId, nextEffect()); 2706 } 2707 runVibrate(commonOptions, combination.combine()); 2708 return 0; 2709 } 2710 runXml()2711 private int runXml() { 2712 CommonOptions commonOptions = new CommonOptions(); 2713 String xml = getNextArgRequired(); 2714 CombinedVibration vibration = parseXml(xml); 2715 runVibrate(commonOptions, vibration); 2716 return 0; 2717 } 2718 runCancel()2719 private int runCancel() { 2720 // Cancel is only needed if the vibration was run in the background, otherwise it's 2721 // terminated by the shell command ending. In these cases, the token was that of the 2722 // service rather than the client. 2723 cancelVibrate(VibrationAttributes.USAGE_FILTER_MATCH_ALL, VibratorManagerService.this); 2724 return 0; 2725 } 2726 runHapticFeedback()2727 private int runHapticFeedback() { 2728 CommonOptions commonOptions = new CommonOptions(); 2729 int constant = parseInt(getNextArgRequired(), "Expected haptic feedback constant id"); 2730 2731 IBinder deathBinder = commonOptions.background ? VibratorManagerService.this 2732 : mShellCallbacksToken; 2733 int flags = commonOptions.force 2734 ? HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING : 0; 2735 HalVibration vib = performHapticFeedbackInternal(Binder.getCallingUid(), 2736 Context.DEVICE_ID_DEFAULT, SHELL_PACKAGE_NAME, constant, 2737 /* reason= */ commonOptions.description, deathBinder, flags, /* privFlags */ 0); 2738 maybeWaitOnVibration(vib, commonOptions); 2739 2740 return 0; 2741 } 2742 nextEffect()2743 private VibrationEffect nextEffect() { 2744 VibrationEffect.Composition composition = VibrationEffect.startComposition(); 2745 String nextArg; 2746 2747 while ((nextArg = peekNextArg()) != null) { 2748 if ("oneshot".equals(nextArg)) { 2749 addOneShotToComposition(composition); 2750 } else if ("waveform".equals(nextArg)) { 2751 addWaveformToComposition(composition); 2752 } else if ("prebaked".equals(nextArg)) { 2753 addPrebakedToComposition(composition); 2754 } else if ("primitives".equals(nextArg)) { 2755 addPrimitivesToComposition(composition); 2756 } else if ("envelope".equals(nextArg)) { 2757 addEnvelopeToComposition(composition); 2758 } else { 2759 // nextArg is not an effect, finish reading. 2760 break; 2761 } 2762 } 2763 2764 return composition.compose(); 2765 } 2766 addOneShotToComposition(VibrationEffect.Composition composition)2767 private void addOneShotToComposition(VibrationEffect.Composition composition) { 2768 boolean hasAmplitude = false; 2769 int delay = 0; 2770 2771 getNextArgRequired(); // consume "oneshot" 2772 String nextOption; 2773 while ((nextOption = getNextOption()) != null) { 2774 if ("-a".equals(nextOption)) { 2775 hasAmplitude = true; 2776 } else if ("-w".equals(nextOption)) { 2777 delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); 2778 } 2779 } 2780 2781 long duration = parseInt(getNextArgRequired(), "Expected one-shot duration millis"); 2782 int amplitude = hasAmplitude 2783 ? parseInt(getNextArgRequired(), "Expected one-shot amplitude") 2784 : VibrationEffect.DEFAULT_AMPLITUDE; 2785 composition.addOffDuration(Duration.ofMillis(delay)); 2786 composition.addEffect(VibrationEffect.createOneShot(duration, amplitude)); 2787 } 2788 2789 private interface EnvelopeBuilder { setInitialSharpness(float sharpness)2790 void setInitialSharpness(float sharpness); addControlPoint(float intensity, float sharpness, long duration)2791 void addControlPoint(float intensity, float sharpness, long duration); reset(float initialSharpness)2792 void reset(float initialSharpness); build()2793 VibrationEffect build(); 2794 } 2795 2796 private static class BasicEnveloperBuilderWrapper implements EnvelopeBuilder { 2797 private VibrationEffect.BasicEnvelopeBuilder mBuilder = 2798 new VibrationEffect.BasicEnvelopeBuilder(); 2799 2800 @Override setInitialSharpness(float sharpness)2801 public void setInitialSharpness(float sharpness) { 2802 mBuilder.setInitialSharpness(sharpness); 2803 } 2804 2805 @Override addControlPoint(float intensity, float sharpness, long duration)2806 public void addControlPoint(float intensity, float sharpness, long duration) { 2807 mBuilder.addControlPoint(intensity, sharpness, duration); 2808 } 2809 2810 @Override reset(float initialSharpness)2811 public void reset(float initialSharpness) { 2812 mBuilder = new VibrationEffect.BasicEnvelopeBuilder(); 2813 mBuilder.setInitialSharpness(initialSharpness); 2814 } 2815 2816 @Override build()2817 public VibrationEffect build() { 2818 return mBuilder.build(); 2819 } 2820 } 2821 2822 private static class AdvancedEnveloperBuilderWrapper implements EnvelopeBuilder { 2823 private VibrationEffect.WaveformEnvelopeBuilder mBuilder = 2824 new VibrationEffect.WaveformEnvelopeBuilder(); 2825 2826 @Override setInitialSharpness(float sharpness)2827 public void setInitialSharpness(float sharpness) { 2828 mBuilder.setInitialFrequencyHz(sharpness); 2829 } 2830 2831 @Override addControlPoint(float intensity, float sharpness, long duration)2832 public void addControlPoint(float intensity, float sharpness, long duration) { 2833 mBuilder.addControlPoint(intensity, sharpness, duration); 2834 } 2835 2836 @Override reset(float initialSharpness)2837 public void reset(float initialSharpness) { 2838 mBuilder = new VibrationEffect.WaveformEnvelopeBuilder(); 2839 mBuilder.setInitialFrequencyHz(initialSharpness); 2840 } 2841 2842 @Override build()2843 public VibrationEffect build() { 2844 return mBuilder.build(); 2845 } 2846 } 2847 addEnvelopeToComposition(VibrationEffect.Composition composition)2848 private void addEnvelopeToComposition(VibrationEffect.Composition composition) { 2849 getNextArgRequired(); // consume "envelope" 2850 int repeat = -1; 2851 float initialSharpness = Float.NaN; 2852 VibrationEffect preamble = null; 2853 boolean isAdvanced = false; 2854 String nextOption; 2855 while ((nextOption = getNextOption()) != null) { 2856 switch (nextOption) { 2857 case "-a" -> isAdvanced = true; 2858 case "-i" -> initialSharpness = parseFloat(getNextArgRequired(), 2859 "Expected initial sharpness after -i"); 2860 case "-r" -> repeat = parseInt(getNextArgRequired(), 2861 "Expected repeat index after -r"); 2862 } 2863 } 2864 2865 EnvelopeBuilder builder = isAdvanced ? new AdvancedEnveloperBuilderWrapper() 2866 : new BasicEnveloperBuilderWrapper(); 2867 2868 if (!Float.isNaN(initialSharpness)) { 2869 builder.setInitialSharpness(initialSharpness); 2870 } 2871 2872 int duration, pos = 0; 2873 float intensity, sharpness = 0f; 2874 String nextArg; 2875 while ((nextArg = peekNextArg()) != null) { 2876 if (pos > 0 && pos == repeat) { 2877 preamble = builder.build(); 2878 builder.reset(sharpness); 2879 } 2880 try { 2881 duration = Integer.parseInt(nextArg); 2882 getNextArgRequired(); // consume the duration 2883 } catch (NumberFormatException e) { 2884 // nextArg is not a duration, finish reading. 2885 break; 2886 } 2887 intensity = parseFloat(getNextArgRequired(), "Expected envelope intensity"); 2888 sharpness = parseFloat(getNextArgRequired(), "Expected envelope sharpness"); 2889 builder.addControlPoint(intensity, sharpness, duration); 2890 pos++; 2891 } 2892 2893 if (repeat >= 0) { 2894 if (preamble == null) { 2895 composition.addEffect(VibrationEffect.createRepeatingEffect(builder.build())); 2896 } else { 2897 composition.addEffect( 2898 VibrationEffect.createRepeatingEffect(preamble, builder.build())); 2899 } 2900 return; 2901 } 2902 2903 composition.addEffect(builder.build()); 2904 } 2905 addWaveformToComposition(VibrationEffect.Composition composition)2906 private void addWaveformToComposition(VibrationEffect.Composition composition) { 2907 boolean hasAmplitudes = false; 2908 boolean hasFrequencies = false; 2909 boolean isContinuous = false; 2910 int repeat = -1; 2911 int delay = 0; 2912 2913 getNextArgRequired(); // consume "waveform" 2914 String nextOption; 2915 while ((nextOption = getNextOption()) != null) { 2916 switch (nextOption) { 2917 case "-a" -> hasAmplitudes = true; 2918 case "-f" -> hasFrequencies = true; 2919 case "-c" -> isContinuous = true; 2920 case "-r" -> repeat = parseInt(getNextArgRequired(), 2921 "Expected repeat index after -r"); 2922 case "-w" -> delay = parseInt(getNextArgRequired(), 2923 "Expected delay millis after -w"); 2924 } 2925 } 2926 List<Integer> durations = new ArrayList<>(); 2927 List<Float> amplitudes = new ArrayList<>(); 2928 List<Float> frequencies = new ArrayList<>(); 2929 2930 float nextAmplitude = 0; 2931 String nextArg; 2932 while ((nextArg = peekNextArg()) != null) { 2933 try { 2934 durations.add(Integer.parseInt(nextArg)); 2935 getNextArgRequired(); // consume the duration 2936 } catch (NumberFormatException e) { 2937 // nextArg is not a duration, finish reading. 2938 break; 2939 } 2940 if (hasAmplitudes) { 2941 int amplitude = parseInt(getNextArgRequired(), "Expected waveform amplitude"); 2942 amplitudes.add((float) amplitude / VibrationEffect.MAX_AMPLITUDE); 2943 } else { 2944 amplitudes.add(nextAmplitude); 2945 nextAmplitude = 1 - nextAmplitude; 2946 } 2947 if (hasFrequencies) { 2948 frequencies.add( 2949 parseFloat(getNextArgRequired(), "Expected waveform frequency")); 2950 } 2951 } 2952 2953 // Add delay before the waveform. 2954 composition.addOffDuration(Duration.ofMillis(delay)); 2955 2956 VibrationEffect.WaveformBuilder waveform = VibrationEffect.startWaveform(); 2957 for (int i = 0; i < durations.size(); i++) { 2958 Duration transitionDuration = isContinuous 2959 ? Duration.ofMillis(durations.get(i)) 2960 : Duration.ZERO; 2961 Duration sustainDuration = isContinuous 2962 ? Duration.ZERO 2963 : Duration.ofMillis(durations.get(i)); 2964 2965 if (hasFrequencies) { 2966 waveform.addTransition(transitionDuration, targetAmplitude(amplitudes.get(i)), 2967 targetFrequency(frequencies.get(i))); 2968 } else { 2969 waveform.addTransition(transitionDuration, targetAmplitude(amplitudes.get(i))); 2970 } 2971 if (!sustainDuration.isZero()) { 2972 // Add sustain only takes positive durations. Skip this since we already 2973 // did a transition to the desired values (even when duration is zero). 2974 waveform.addSustain(sustainDuration); 2975 } 2976 2977 if ((i > 0) && (i == repeat)) { 2978 // Add segment that is not repeated to the composition and reset builder. 2979 composition.addEffect(waveform.build()); 2980 2981 if (hasFrequencies) { 2982 waveform = VibrationEffect.startWaveform(targetAmplitude(amplitudes.get(i)), 2983 targetFrequency(frequencies.get(i))); 2984 } else { 2985 waveform = VibrationEffect.startWaveform( 2986 targetAmplitude(amplitudes.get(i))); 2987 } 2988 } 2989 } 2990 if (repeat < 0) { 2991 composition.addEffect(waveform.build()); 2992 } else { 2993 // The waveform was already split at the repeat index, just repeat what remains. 2994 composition.repeatEffectIndefinitely(waveform.build()); 2995 } 2996 } 2997 addPrebakedToComposition(VibrationEffect.Composition composition)2998 private void addPrebakedToComposition(VibrationEffect.Composition composition) { 2999 boolean shouldFallback = false; 3000 int delay = 0; 3001 3002 getNextArgRequired(); // consume "prebaked" 3003 String nextOption; 3004 while ((nextOption = getNextOption()) != null) { 3005 if ("-b".equals(nextOption)) { 3006 shouldFallback = true; 3007 } else if ("-w".equals(nextOption)) { 3008 delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); 3009 } 3010 } 3011 3012 int effectId = parseInt(getNextArgRequired(), "Expected prebaked effect id"); 3013 composition.addOffDuration(Duration.ofMillis(delay)); 3014 composition.addEffect(VibrationEffect.get(effectId, shouldFallback)); 3015 } 3016 addPrimitivesToComposition(VibrationEffect.Composition composition)3017 private void addPrimitivesToComposition(VibrationEffect.Composition composition) { 3018 getNextArgRequired(); // consume "primitives" 3019 while (peekNextArg() != null) { 3020 int delay = 0; 3021 float scale = 1f; 3022 int delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE; 3023 3024 String nextOption; 3025 while ((nextOption = getNextOption()) != null) { 3026 if ("-s".equals(nextOption)) { 3027 scale = parseFloat(getNextArgRequired(), "Expected scale after -s"); 3028 } else if ("-o".equals(nextOption)) { 3029 delayType = VibrationEffect.Composition.DELAY_TYPE_RELATIVE_START_OFFSET; 3030 delay = parseInt(getNextArgRequired(), "Expected offset millis after -o"); 3031 } else if ("-w".equals(nextOption)) { 3032 delayType = PrimitiveSegment.DEFAULT_DELAY_TYPE; 3033 delay = parseInt(getNextArgRequired(), "Expected delay millis after -w"); 3034 } 3035 } 3036 try { 3037 String nextArg = peekNextArg(); // Just in case this is not a primitive. 3038 composition.addPrimitive(Integer.parseInt(nextArg), scale, delay, delayType); 3039 getNextArgRequired(); // consume the primitive id 3040 } catch (NumberFormatException | NullPointerException e) { 3041 // nextArg is not describing a primitive, leave it to be consumed by outer loops 3042 break; 3043 } 3044 } 3045 } 3046 createVibrationAttributes(CommonOptions commonOptions)3047 private VibrationAttributes createVibrationAttributes(CommonOptions commonOptions) { 3048 // This will bypass user settings, Do Not Disturb and other interruption policies. 3049 final int flags = commonOptions.force ? ATTRIBUTES_ALL_BYPASS_FLAGS : 0; 3050 return new VibrationAttributes.Builder() 3051 .setFlags(flags) 3052 // Used to allow vibrations when the adb shell process is running in background. 3053 // This will apply the NOTIFICATION_VIBRATION_INTENSITY setting. 3054 .setUsage(VibrationAttributes.USAGE_COMMUNICATION_REQUEST) 3055 .build(); 3056 } 3057 parseXml(String xml)3058 private CombinedVibration parseXml(String xml) { 3059 try { 3060 ParsedVibration parsedVibration = 3061 VibrationXmlParser.parseDocument(new StringReader(xml)); 3062 VibratorInfo combinedVibratorInfo = getCombinedVibratorInfo(); 3063 if (combinedVibratorInfo == null) { 3064 throw new IllegalStateException("No vibrator info available to parse XML"); 3065 } 3066 VibrationEffect effect = parsedVibration.resolve(combinedVibratorInfo); 3067 if (effect == null) { 3068 throw new IllegalArgumentException("Parsed XML cannot be resolved: " + xml); 3069 } 3070 return CombinedVibration.createParallel(effect); 3071 } catch (IOException e) { 3072 throw new RuntimeException("Error parsing XML: " + xml, e); 3073 } 3074 } 3075 maybeWaitOnVibration(HalVibration vib, CommonOptions commonOptions)3076 private void maybeWaitOnVibration(HalVibration vib, CommonOptions commonOptions) { 3077 if (vib != null && !commonOptions.background) { 3078 try { 3079 // Waits for the client vibration to finish, but the VibrationThread may still 3080 // do cleanup after this. 3081 vib.waitForEnd(); 3082 // Wait for vibration clean up and possible ramp down before ending. 3083 mVibrationThread.waitForThreadIdle( 3084 mVibrationSettings.getRampDownDuration() + VIBRATION_END_TIMEOUT_MS); 3085 } catch (InterruptedException e) { 3086 } 3087 } 3088 } 3089 parseInt(String text, String errorMessage)3090 private static int parseInt(String text, String errorMessage) { 3091 try { 3092 return Integer.parseInt(text); 3093 } catch (NumberFormatException | NullPointerException e) { 3094 throw new IllegalArgumentException(errorMessage, e); 3095 } 3096 } 3097 parseFloat(String text, String errorMessage)3098 private static float parseFloat(String text, String errorMessage) { 3099 try { 3100 return Float.parseFloat(text); 3101 } catch (NumberFormatException | NullPointerException e) { 3102 throw new IllegalArgumentException(errorMessage, e); 3103 } 3104 } 3105 3106 @Override onHelp()3107 public void onHelp() { 3108 try (PrintWriter pw = getOutPrintWriter();) { 3109 pw.println("Vibrator Manager commands:"); 3110 pw.println(" help"); 3111 pw.println(" Prints this help text."); 3112 pw.println(" list"); 3113 pw.println(" Prints device vibrator ids; does not include input devices."); 3114 pw.println(" synced [options] <effect>..."); 3115 pw.println(" Vibrates effect on all vibrators in sync."); 3116 pw.println(" combined [options] (-v <vibrator-id> <effect>...)..."); 3117 pw.println(" Vibrates different effects on each vibrator in sync."); 3118 pw.println(" sequential [options] (-v <vibrator-id> <effect>...)..."); 3119 pw.println(" Vibrates different effects on each vibrator in sequence."); 3120 pw.println(" xml [options] <xml>"); 3121 pw.println(" Vibrates using combined vibration described in given XML string"); 3122 pw.println(" on all vibrators in sync. The XML could be:"); 3123 pw.println(" A single <vibration-effect>, or"); 3124 pw.println(" A <vibration-select> containing multiple effects."); 3125 pw.println(" feedback [options] <constant>"); 3126 pw.println(" Performs a haptic feedback with the given constant."); 3127 pw.println(" cancel"); 3128 pw.println(" Cancels any active vibration"); 3129 pw.println(""); 3130 pw.println("Effect commands:"); 3131 pw.println(" oneshot [-w delay] [-a] <duration> [<amplitude>]"); 3132 pw.println(" Vibrates for duration milliseconds."); 3133 pw.println(" If -w is provided, the effect will be played after the specified"); 3134 pw.println(" wait time in milliseconds."); 3135 pw.println(" If -a is provided, the command accepts a second argument for "); 3136 pw.println(" amplitude, in a scale of 1-255."); 3137 pw.print(" waveform [-w delay] [-r index] [-a] [-f] [-c] "); 3138 pw.println("(<duration> [<amplitude>] [<frequency>])..."); 3139 pw.println(" Vibrates for durations and amplitudes in list."); 3140 pw.println(" If -w is provided, the effect will be played after the specified"); 3141 pw.println(" wait time in milliseconds."); 3142 pw.println(" If -r is provided, the waveform loops back to the specified"); 3143 pw.println(" index (e.g. 0 loops from the beginning)."); 3144 pw.println(" If -a is provided, the command expects amplitude to follow each"); 3145 pw.println(" duration; otherwise, it accepts durations only and alternates"); 3146 pw.println(" off/on."); 3147 pw.println(" If -f is provided, the command expects frequency to follow each"); 3148 pw.println(" amplitude or duration; otherwise, it uses resonant frequency."); 3149 pw.println(" If -c is provided, the waveform is continuous and will ramp"); 3150 pw.println(" between values; otherwise each entry is a fixed step."); 3151 pw.println(" Duration is in milliseconds; amplitude is a scale of 1-255;"); 3152 pw.println(" frequency is an absolute value in hertz."); 3153 pw.print(" envelope [-a] [-i initial sharpness] [-r index] "); 3154 pw.println("[<duration1> <intensity1> <sharpness1>]..."); 3155 pw.println(" Generates a vibration pattern based on a series of duration, "); 3156 pw.println(" intensity, and sharpness values. The total vibration time is "); 3157 pw.println(" the sum of all durations."); 3158 pw.println(" If -a is provided, the waveform will use the advanced APIs to "); 3159 pw.println(" generate the vibration pattern and the input parameters "); 3160 pw.println(" become [<duration1> <amplitude1> <frequency1>]."); 3161 pw.println(" If -i is provided, the waveform will have an initial sharpness "); 3162 pw.println(" it will start from."); 3163 pw.println(" If -r is provided, the waveform loops back to the specified index"); 3164 pw.println(" (e.g. 0 loops from the beginning)."); 3165 pw.println(" prebaked [-w delay] [-b] <effect-id>"); 3166 pw.println(" Vibrates with prebaked effect."); 3167 pw.println(" If -w is provided, the effect will be played after the specified"); 3168 pw.println(" wait time in milliseconds."); 3169 pw.println(" If -b is provided, the prebaked fallback effect will be played if"); 3170 pw.println(" the device doesn't support the given effect-id."); 3171 pw.print(" primitives ([-w delay] [-o time] [-s scale]"); 3172 pw.println("<primitive-id> [<scale>])..."); 3173 pw.println(" Vibrates with a composed effect."); 3174 pw.println(" If -w is provided, the next primitive will be played after the "); 3175 pw.println(" specified wait time in milliseconds."); 3176 pw.println(" If -o is provided, the next primitive will be played at the "); 3177 pw.println(" specified start offset time in milliseconds."); 3178 pw.println(" If -s is provided, the next primitive will be played with the"); 3179 pw.println(" specified amplitude scale, in a scale of [0,1]."); 3180 pw.println(""); 3181 pw.println("Common Options:"); 3182 pw.println(" -f"); 3183 pw.println(" Force. Ignore Do Not Disturb setting."); 3184 pw.println(" -B"); 3185 pw.println(" Run in the background; without this option the shell cmd will"); 3186 pw.println(" block until the vibration has completed."); 3187 pw.println(" -d <description>"); 3188 pw.println(" Add description to the vibration."); 3189 pw.println(""); 3190 pw.println("Notes"); 3191 pw.println(" Vibrations triggered by these commands will be ignored when"); 3192 pw.println(" device is on DND (Do Not Disturb) mode; notification strength"); 3193 pw.println(" user settings will be applied for scale."); 3194 pw.println(""); 3195 } 3196 } 3197 } 3198 } 3199