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