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