• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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