• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 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;
18 
19 import android.app.ActivityManager;
20 import android.app.AppOpsManager;
21 import android.app.IUidObserver;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.content.pm.PackageManager;
27 import android.content.res.Resources;
28 import android.database.ContentObserver;
29 import android.hardware.input.InputManager;
30 import android.hardware.vibrator.V1_0.EffectStrength;
31 import android.icu.text.DateFormat;
32 import android.media.AudioAttributes;
33 import android.media.AudioManager;
34 import android.os.BatteryStats;
35 import android.os.Binder;
36 import android.os.ExternalVibration;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.os.IExternalVibratorService;
40 import android.os.IVibratorService;
41 import android.os.PowerManager;
42 import android.os.PowerManager.ServiceType;
43 import android.os.PowerManagerInternal;
44 import android.os.PowerSaveState;
45 import android.os.Process;
46 import android.os.RemoteException;
47 import android.os.ResultReceiver;
48 import android.os.ServiceManager;
49 import android.os.ShellCallback;
50 import android.os.ShellCommand;
51 import android.os.SystemClock;
52 import android.os.Trace;
53 import android.os.UserHandle;
54 import android.os.VibrationEffect;
55 import android.os.Vibrator;
56 import android.os.WorkSource;
57 import android.provider.DeviceConfig;
58 import android.provider.Settings;
59 import android.provider.Settings.SettingNotFoundException;
60 import android.util.DebugUtils;
61 import android.util.Slog;
62 import android.util.SparseArray;
63 import android.util.StatsLog;
64 import android.view.InputDevice;
65 
66 import com.android.internal.annotations.GuardedBy;
67 import com.android.internal.app.IBatteryStats;
68 import com.android.internal.util.DumpUtils;
69 
70 import java.io.FileDescriptor;
71 import java.io.PrintWriter;
72 import java.util.ArrayList;
73 import java.util.Date;
74 import java.util.LinkedList;
75 
76 public class VibratorService extends IVibratorService.Stub
77         implements InputManager.InputDeviceListener {
78     private static final String TAG = "VibratorService";
79     private static final boolean DEBUG = false;
80     private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
81     private static final String EXTERNAL_VIBRATOR_SERVICE = "external_vibrator_service";
82     private static final String RAMPING_RINGER_ENABLED = "ramping_ringer_enabled";
83 
84     private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 };
85 
86     // Scale levels. Each level, except MUTE, is defined as the delta between the current setting
87     // and the default intensity for that type of vibration (i.e. current - default).
88     private static final int SCALE_MUTE = IExternalVibratorService.SCALE_MUTE; // -100
89     private static final int SCALE_VERY_LOW = IExternalVibratorService.SCALE_VERY_LOW; // -2
90     private static final int SCALE_LOW = IExternalVibratorService.SCALE_LOW; // -1
91     private static final int SCALE_NONE = IExternalVibratorService.SCALE_NONE; // 0
92     private static final int SCALE_HIGH = IExternalVibratorService.SCALE_HIGH; // 1
93     private static final int SCALE_VERY_HIGH = IExternalVibratorService.SCALE_VERY_HIGH; // 2
94 
95     // Gamma adjustments for scale levels.
96     private static final float SCALE_VERY_LOW_GAMMA = 2.0f;
97     private static final float SCALE_LOW_GAMMA = 1.5f;
98     private static final float SCALE_NONE_GAMMA = 1.0f;
99     private static final float SCALE_HIGH_GAMMA = 0.5f;
100     private static final float SCALE_VERY_HIGH_GAMMA = 0.25f;
101 
102     // Max amplitudes for scale levels. If one is not listed, then the max amplitude is the default
103     // max amplitude.
104     private static final int SCALE_VERY_LOW_MAX_AMPLITUDE = 168; // 2/3 * 255
105     private static final int SCALE_LOW_MAX_AMPLITUDE = 192; // 3/4 * 255
106 
107     // If a vibration is playing for longer than 5s, it's probably not haptic feedback.
108     private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
109 
110 
111     // A mapping from the intensity adjustment to the scaling to apply, where the intensity
112     // adjustment is defined as the delta between the default intensity level and the user selected
113     // intensity level. It's important that we apply the scaling on the delta between the two so
114     // that the default intensity level applies no scaling to application provided effects.
115     private final SparseArray<ScaleLevel> mScaleLevels;
116     private final LinkedList<VibrationInfo> mPreviousRingVibrations;
117     private final LinkedList<VibrationInfo> mPreviousNotificationVibrations;
118     private final LinkedList<VibrationInfo> mPreviousAlarmVibrations;
119     private final LinkedList<ExternalVibration> mPreviousExternalVibrations;
120     private final LinkedList<VibrationInfo> mPreviousVibrations;
121     private final int mPreviousVibrationsLimit;
122     private final boolean mAllowPriorityVibrationsInLowPowerMode;
123     private final boolean mSupportsAmplitudeControl;
124     private final boolean mSupportsExternalControl;
125     private final int mDefaultVibrationAmplitude;
126     private final SparseArray<VibrationEffect> mFallbackEffects;
127     private final SparseArray<Integer> mProcStatesCache = new SparseArray();
128     private final WorkSource mTmpWorkSource = new WorkSource();
129     private final Handler mH = new Handler();
130     private final Object mLock = new Object();
131 
132     private final Context mContext;
133     private final PowerManager.WakeLock mWakeLock;
134     private final AppOpsManager mAppOps;
135     private final IBatteryStats mBatteryStatsService;
136     private PowerManagerInternal mPowerManagerInternal;
137     private InputManager mIm;
138     private Vibrator mVibrator;
139     private SettingsObserver mSettingObserver;
140 
141     private volatile VibrateThread mThread;
142 
143     // mInputDeviceVibrators lock should be acquired after mLock, if both are
144     // to be acquired
145     private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
146     private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
147     private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
148 
149     @GuardedBy("mLock")
150     private Vibration mCurrentVibration;
151     private int mCurVibUid = -1;
152     private ExternalVibration mCurrentExternalVibration;
153     private boolean mVibratorUnderExternalControl;
154     private boolean mLowPowerMode;
155     private int mHapticFeedbackIntensity;
156     private int mNotificationIntensity;
157     private int mRingIntensity;
158 
vibratorExists()159     static native boolean vibratorExists();
vibratorInit()160     static native void vibratorInit();
vibratorOn(long milliseconds)161     static native void vibratorOn(long milliseconds);
vibratorOff()162     static native void vibratorOff();
vibratorSupportsAmplitudeControl()163     static native boolean vibratorSupportsAmplitudeControl();
vibratorSetAmplitude(int amplitude)164     static native void vibratorSetAmplitude(int amplitude);
vibratorPerformEffect(long effect, long strength)165     static native long vibratorPerformEffect(long effect, long strength);
vibratorSupportsExternalControl()166     static native boolean vibratorSupportsExternalControl();
vibratorSetExternalControl(boolean enabled)167     static native void vibratorSetExternalControl(boolean enabled);
168 
169     private final IUidObserver mUidObserver = new IUidObserver.Stub() {
170         @Override public void onUidStateChanged(int uid, int procState, long procStateSeq) {
171             mProcStatesCache.put(uid, procState);
172         }
173 
174         @Override public void onUidGone(int uid, boolean disabled) {
175             mProcStatesCache.delete(uid);
176         }
177 
178         @Override public void onUidActive(int uid) {
179         }
180 
181         @Override public void onUidIdle(int uid, boolean disabled) {
182         }
183 
184         @Override public void onUidCachedChanged(int uid, boolean cached) {
185         }
186     };
187 
188     private class Vibration implements IBinder.DeathRecipient {
189         public final IBinder token;
190         // Start time in CLOCK_BOOTTIME base.
191         public final long startTime;
192         // Start time in unix epoch time. Only to be used for debugging purposes and to correlate
193         // with other system events, any duration calculations should be done use startTime so as
194         // not to be affected by discontinuities created by RTC adjustments.
195         public final long startTimeDebug;
196         public final int usageHint;
197         public final int uid;
198         public final String opPkg;
199         public final String reason;
200 
201         // The actual effect to be played.
202         public VibrationEffect effect;
203         // The original effect that was requested. This is non-null only when the original effect
204         // differs from the effect that's being played. Typically these two things differ because
205         // the effect was scaled based on the users vibration intensity settings.
206         public VibrationEffect originalEffect;
207 
Vibration(IBinder token, VibrationEffect effect, int usageHint, int uid, String opPkg, String reason)208         private Vibration(IBinder token, VibrationEffect effect,
209                 int usageHint, int uid, String opPkg, String reason) {
210             this.token = token;
211             this.effect = effect;
212             this.startTime = SystemClock.elapsedRealtime();
213             this.startTimeDebug = System.currentTimeMillis();
214             this.usageHint = usageHint;
215             this.uid = uid;
216             this.opPkg = opPkg;
217             this.reason = reason;
218         }
219 
binderDied()220         public void binderDied() {
221             synchronized (mLock) {
222                 if (this == mCurrentVibration) {
223                     doCancelVibrateLocked();
224                 }
225             }
226         }
227 
hasTimeoutLongerThan(long millis)228         public boolean hasTimeoutLongerThan(long millis) {
229             final long duration = effect.getDuration();
230             return duration >= 0 && duration > millis;
231         }
232 
isHapticFeedback()233         public boolean isHapticFeedback() {
234             if (VibratorService.this.isHapticFeedback(usageHint)) {
235                 return true;
236             }
237             if (effect instanceof VibrationEffect.Prebaked) {
238                 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
239                 switch (prebaked.getId()) {
240                     case VibrationEffect.EFFECT_CLICK:
241                     case VibrationEffect.EFFECT_DOUBLE_CLICK:
242                     case VibrationEffect.EFFECT_HEAVY_CLICK:
243                     case VibrationEffect.EFFECT_TEXTURE_TICK:
244                     case VibrationEffect.EFFECT_TICK:
245                     case VibrationEffect.EFFECT_POP:
246                     case VibrationEffect.EFFECT_THUD:
247                         return true;
248                     default:
249                         Slog.w(TAG, "Unknown prebaked vibration effect, "
250                                 + "assuming it isn't haptic feedback.");
251                         return false;
252                 }
253             }
254             final long duration = effect.getDuration();
255             return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION;
256         }
257 
isNotification()258         public boolean isNotification() {
259             return VibratorService.this.isNotification(usageHint);
260         }
261 
isRingtone()262         public boolean isRingtone() {
263             return VibratorService.this.isRingtone(usageHint);
264         }
265 
isAlarm()266         public boolean isAlarm() {
267             return VibratorService.this.isAlarm(usageHint);
268         }
269 
isFromSystem()270         public boolean isFromSystem() {
271             return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
272         }
273 
toInfo()274         public VibrationInfo toInfo() {
275             return new VibrationInfo(
276                     startTimeDebug, effect, originalEffect, usageHint, uid, opPkg, reason);
277         }
278     }
279 
280     private static class VibrationInfo {
281         private final long mStartTimeDebug;
282         private final VibrationEffect mEffect;
283         private final VibrationEffect mOriginalEffect;
284         private final int mUsageHint;
285         private final int mUid;
286         private final String mOpPkg;
287         private final String mReason;
288 
VibrationInfo(long startTimeDebug, VibrationEffect effect, VibrationEffect originalEffect, int usageHint, int uid, String opPkg, String reason)289         public VibrationInfo(long startTimeDebug, VibrationEffect effect,
290                 VibrationEffect originalEffect, int usageHint, int uid,
291                 String opPkg, String reason) {
292             mStartTimeDebug = startTimeDebug;
293             mEffect = effect;
294             mOriginalEffect = originalEffect;
295             mUsageHint = usageHint;
296             mUid = uid;
297             mOpPkg = opPkg;
298             mReason = reason;
299         }
300 
301         @Override
toString()302         public String toString() {
303             return new StringBuilder()
304                     .append("startTime: ")
305                     .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
306                     .append(", effect: ")
307                     .append(mEffect)
308                     .append(", originalEffect: ")
309                     .append(mOriginalEffect)
310                     .append(", usageHint: ")
311                     .append(mUsageHint)
312                     .append(", uid: ")
313                     .append(mUid)
314                     .append(", opPkg: ")
315                     .append(mOpPkg)
316                     .append(", reason: ")
317                     .append(mReason)
318                     .toString();
319         }
320     }
321 
322     private static final class ScaleLevel {
323         public final float gamma;
324         public final int maxAmplitude;
325 
ScaleLevel(float gamma)326         public ScaleLevel(float gamma) {
327             this(gamma, VibrationEffect.MAX_AMPLITUDE);
328         }
329 
ScaleLevel(float gamma, int maxAmplitude)330         public ScaleLevel(float gamma, int maxAmplitude) {
331             this.gamma = gamma;
332             this.maxAmplitude = maxAmplitude;
333         }
334 
335         @Override
toString()336         public String toString() {
337             return "ScaleLevel{gamma=" + gamma + ", maxAmplitude=" + maxAmplitude + "}";
338         }
339     }
340 
VibratorService(Context context)341     VibratorService(Context context) {
342         vibratorInit();
343         // Reset the hardware to a default state, in case this is a runtime
344         // restart instead of a fresh boot.
345         vibratorOff();
346 
347         mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
348         mSupportsExternalControl = vibratorSupportsExternalControl();
349 
350         mContext = context;
351         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
352         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
353         mWakeLock.setReferenceCounted(true);
354 
355         mAppOps = mContext.getSystemService(AppOpsManager.class);
356         mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
357                 BatteryStats.SERVICE_NAME));
358 
359         mPreviousVibrationsLimit = mContext.getResources().getInteger(
360                 com.android.internal.R.integer.config_previousVibrationsDumpLimit);
361 
362         mDefaultVibrationAmplitude = mContext.getResources().getInteger(
363                 com.android.internal.R.integer.config_defaultVibrationAmplitude);
364 
365         mAllowPriorityVibrationsInLowPowerMode = mContext.getResources().getBoolean(
366                 com.android.internal.R.bool.config_allowPriorityVibrationsInLowPowerMode);
367 
368         mPreviousRingVibrations = new LinkedList<>();
369         mPreviousNotificationVibrations = new LinkedList<>();
370         mPreviousAlarmVibrations = new LinkedList<>();
371         mPreviousVibrations = new LinkedList<>();
372         mPreviousExternalVibrations = new LinkedList<>();
373 
374         IntentFilter filter = new IntentFilter();
375         filter.addAction(Intent.ACTION_SCREEN_OFF);
376         context.registerReceiver(mIntentReceiver, filter);
377 
378         VibrationEffect clickEffect = createEffectFromResource(
379                 com.android.internal.R.array.config_virtualKeyVibePattern);
380         VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
381                 DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/);
382         VibrationEffect heavyClickEffect = createEffectFromResource(
383                 com.android.internal.R.array.config_longPressVibePattern);
384         VibrationEffect tickEffect = createEffectFromResource(
385                 com.android.internal.R.array.config_clockTickVibePattern);
386 
387         mFallbackEffects = new SparseArray<>();
388         mFallbackEffects.put(VibrationEffect.EFFECT_CLICK, clickEffect);
389         mFallbackEffects.put(VibrationEffect.EFFECT_DOUBLE_CLICK, doubleClickEffect);
390         mFallbackEffects.put(VibrationEffect.EFFECT_TICK, tickEffect);
391         mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, heavyClickEffect);
392 
393         mFallbackEffects.put(VibrationEffect.EFFECT_TEXTURE_TICK,
394                 VibrationEffect.get(VibrationEffect.EFFECT_TICK, false));
395 
396         mScaleLevels = new SparseArray<>();
397         mScaleLevels.put(SCALE_VERY_LOW,
398                 new ScaleLevel(SCALE_VERY_LOW_GAMMA, SCALE_VERY_LOW_MAX_AMPLITUDE));
399         mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_LOW_GAMMA, SCALE_LOW_MAX_AMPLITUDE));
400         mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_NONE_GAMMA));
401         mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_HIGH_GAMMA));
402         mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_VERY_HIGH_GAMMA));
403 
404         ServiceManager.addService(EXTERNAL_VIBRATOR_SERVICE, new ExternalVibratorService());
405     }
406 
createEffectFromResource(int resId)407     private VibrationEffect createEffectFromResource(int resId) {
408         long[] timings = getLongIntArray(mContext.getResources(), resId);
409         return createEffectFromTimings(timings);
410     }
411 
createEffectFromTimings(long[] timings)412     private static VibrationEffect createEffectFromTimings(long[] timings) {
413         if (timings == null || timings.length == 0) {
414             return null;
415         } else if (timings.length == 1) {
416             return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE);
417         } else {
418             return VibrationEffect.createWaveform(timings, -1);
419         }
420     }
421 
systemReady()422     public void systemReady() {
423         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
424         try {
425             mIm = mContext.getSystemService(InputManager.class);
426             mVibrator = mContext.getSystemService(Vibrator.class);
427             mSettingObserver = new SettingsObserver(mH);
428 
429             mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
430             mPowerManagerInternal.registerLowPowerModeObserver(
431                     new PowerManagerInternal.LowPowerModeListener() {
432                         @Override
433                         public int getServiceType() {
434                             return ServiceType.VIBRATION;
435                         }
436 
437                         @Override
438                         public void onLowPowerModeChanged(PowerSaveState result) {
439                             updateVibrators();
440                         }
441             });
442 
443             mContext.getContentResolver().registerContentObserver(
444                     Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
445                     true, mSettingObserver, UserHandle.USER_ALL);
446 
447             mContext.getContentResolver().registerContentObserver(
448                     Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY),
449                     true, mSettingObserver, UserHandle.USER_ALL);
450 
451             mContext.getContentResolver().registerContentObserver(
452                     Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
453                     true, mSettingObserver, UserHandle.USER_ALL);
454 
455             mContext.getContentResolver().registerContentObserver(
456                     Settings.System.getUriFor(Settings.System.RING_VIBRATION_INTENSITY),
457                     true, mSettingObserver, UserHandle.USER_ALL);
458 
459             mContext.registerReceiver(new BroadcastReceiver() {
460                 @Override
461                 public void onReceive(Context context, Intent intent) {
462                     updateVibrators();
463                 }
464             }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
465 
466             try {
467                 ActivityManager.getService().registerUidObserver(mUidObserver,
468                         ActivityManager.UID_OBSERVER_PROCSTATE | ActivityManager.UID_OBSERVER_GONE,
469                         ActivityManager.PROCESS_STATE_UNKNOWN, null);
470             } catch (RemoteException e) {
471                 // ignored; both services live in system_server
472             }
473 
474             updateVibrators();
475         } finally {
476             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
477         }
478     }
479 
480     private final class SettingsObserver extends ContentObserver {
SettingsObserver(Handler handler)481         public SettingsObserver(Handler handler) {
482             super(handler);
483         }
484 
485         @Override
onChange(boolean SelfChange)486         public void onChange(boolean SelfChange) {
487             updateVibrators();
488         }
489     }
490 
491     @Override // Binder call
hasVibrator()492     public boolean hasVibrator() {
493         return doVibratorExists();
494     }
495 
496     @Override // Binder call
hasAmplitudeControl()497     public boolean hasAmplitudeControl() {
498         synchronized (mInputDeviceVibrators) {
499             // Input device vibrators don't support amplitude controls yet, but are still used over
500             // the system vibrator when connected.
501             return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty();
502         }
503     }
504 
verifyIncomingUid(int uid)505     private void verifyIncomingUid(int uid) {
506         if (uid == Binder.getCallingUid()) {
507             return;
508         }
509         if (Binder.getCallingPid() == Process.myPid()) {
510             return;
511         }
512         mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
513                 Binder.getCallingPid(), Binder.getCallingUid(), null);
514     }
515 
516     /**
517      * Validate the incoming VibrationEffect.
518      *
519      * We can't throw exceptions here since we might be called from some system_server component,
520      * which would bring the whole system down.
521      *
522      * @return whether the VibrationEffect is valid
523      */
verifyVibrationEffect(VibrationEffect effect)524     private static boolean verifyVibrationEffect(VibrationEffect effect) {
525         if (effect == null) {
526             // Effect must not be null.
527             Slog.wtf(TAG, "effect must not be null");
528             return false;
529         }
530         try {
531             effect.validate();
532         } catch (Exception e) {
533             Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e);
534             return false;
535         }
536         return true;
537     }
538 
getLongIntArray(Resources r, int resid)539     private static long[] getLongIntArray(Resources r, int resid) {
540         int[] ar = r.getIntArray(resid);
541         if (ar == null) {
542             return null;
543         }
544         long[] out = new long[ar.length];
545         for (int i = 0; i < ar.length; i++) {
546             out[i] = ar[i];
547         }
548         return out;
549     }
550 
551     @Override // Binder call
vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason, IBinder token)552     public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, String reason,
553             IBinder token) {
554         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate, reason = " + reason);
555         try {
556             if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
557                     != PackageManager.PERMISSION_GRANTED) {
558                 throw new SecurityException("Requires VIBRATE permission");
559             }
560             if (token == null) {
561                 Slog.e(TAG, "token must not be null");
562                 return;
563             }
564             verifyIncomingUid(uid);
565             if (!verifyVibrationEffect(effect)) {
566                 return;
567             }
568 
569             // If our current vibration is longer than the new vibration and is the same amplitude,
570             // then just let the current one finish.
571             synchronized (mLock) {
572                 if (effect instanceof VibrationEffect.OneShot
573                         && mCurrentVibration != null
574                         && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
575                     VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
576                     VibrationEffect.OneShot currentOneShot =
577                             (VibrationEffect.OneShot) mCurrentVibration.effect;
578                     if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
579                             && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
580                         if (DEBUG) {
581                             Slog.d(TAG,
582                                     "Ignoring incoming vibration in favor of current vibration");
583                         }
584                         return;
585                     }
586                 }
587 
588 
589                 // If something has external control of the vibrator, assume that it's more
590                 // important for now.
591                 if (mCurrentExternalVibration != null) {
592                     if (DEBUG) {
593                         Slog.d(TAG, "Ignoring incoming vibration for current external vibration");
594                     }
595                     return;
596                 }
597 
598                 // If the current vibration is repeating and the incoming one is non-repeating,
599                 // then ignore the non-repeating vibration. This is so that we don't cancel
600                 // vibrations that are meant to grab the attention of the user, like ringtones and
601                 // alarms, in favor of one-shot vibrations that are likely quite short.
602                 if (!isRepeatingVibration(effect)
603                         && mCurrentVibration != null
604                         && isRepeatingVibration(mCurrentVibration.effect)) {
605                     if (DEBUG) {
606                         Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
607                     }
608                     return;
609                 }
610 
611                 Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg, reason);
612                 if (mProcStatesCache.get(uid, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND)
613                         > ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND
614                         && !vib.isNotification() && !vib.isRingtone() && !vib.isAlarm()) {
615                     Slog.e(TAG, "Ignoring incoming vibration as process with"
616                             + " uid = " + uid + " is background,"
617                             + " usage = " + AudioAttributes.usageToString(vib.usageHint));
618                     return;
619                 }
620                 linkVibration(vib);
621                 long ident = Binder.clearCallingIdentity();
622                 try {
623                     doCancelVibrateLocked();
624                     startVibrationLocked(vib);
625                     addToPreviousVibrationsLocked(vib);
626                 } finally {
627                     Binder.restoreCallingIdentity(ident);
628                 }
629             }
630         } finally {
631             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
632         }
633     }
634 
isRepeatingVibration(VibrationEffect effect)635     private static boolean isRepeatingVibration(VibrationEffect effect) {
636         return effect.getDuration() == Long.MAX_VALUE;
637     }
638 
addToPreviousVibrationsLocked(Vibration vib)639     private void addToPreviousVibrationsLocked(Vibration vib) {
640         final LinkedList<VibrationInfo> previousVibrations;
641         if (vib.isRingtone()) {
642             previousVibrations = mPreviousRingVibrations;
643         } else if (vib.isNotification()) {
644             previousVibrations = mPreviousNotificationVibrations;
645         } else if (vib.isAlarm()) {
646             previousVibrations = mPreviousAlarmVibrations;
647         } else {
648             previousVibrations = mPreviousVibrations;
649         }
650 
651         if (previousVibrations.size() > mPreviousVibrationsLimit) {
652             previousVibrations.removeFirst();
653         }
654         previousVibrations.addLast(vib.toInfo());
655     }
656 
657     @Override // Binder call
cancelVibrate(IBinder token)658     public void cancelVibrate(IBinder token) {
659         mContext.enforceCallingOrSelfPermission(
660                 android.Manifest.permission.VIBRATE,
661                 "cancelVibrate");
662 
663         synchronized (mLock) {
664             if (mCurrentVibration != null && mCurrentVibration.token == token) {
665                 if (DEBUG) {
666                     Slog.d(TAG, "Canceling vibration.");
667                 }
668                 long ident = Binder.clearCallingIdentity();
669                 try {
670                     doCancelVibrateLocked();
671                 } finally {
672                     Binder.restoreCallingIdentity(ident);
673                 }
674             }
675         }
676     }
677 
678     private final Runnable mVibrationEndRunnable = new Runnable() {
679         @Override
680         public void run() {
681             onVibrationFinished();
682         }
683     };
684 
685     @GuardedBy("mLock")
doCancelVibrateLocked()686     private void doCancelVibrateLocked() {
687         Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
688         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
689         try {
690             mH.removeCallbacks(mVibrationEndRunnable);
691             if (mThread != null) {
692                 mThread.cancel();
693                 mThread = null;
694             }
695             if (mCurrentExternalVibration != null) {
696                 mCurrentExternalVibration.mute();
697                 mCurrentExternalVibration = null;
698                 setVibratorUnderExternalControl(false);
699             }
700             doVibratorOff();
701             reportFinishVibrationLocked();
702         } finally {
703             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
704         }
705     }
706 
707     // Callback for whenever the current vibration has finished played out
onVibrationFinished()708     public void onVibrationFinished() {
709         if (DEBUG) {
710             Slog.e(TAG, "Vibration finished, cleaning up");
711         }
712         synchronized (mLock) {
713             // Make sure the vibration is really done. This also reports that the vibration is
714             // finished.
715             doCancelVibrateLocked();
716         }
717     }
718 
719     @GuardedBy("mLock")
startVibrationLocked(final Vibration vib)720     private void startVibrationLocked(final Vibration vib) {
721         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
722         try {
723             if (!isAllowedToVibrateLocked(vib)) {
724                 return;
725             }
726 
727             final int intensity = getCurrentIntensityLocked(vib);
728             if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
729                 return;
730             }
731 
732             if (vib.isRingtone() && !shouldVibrateForRingtone()) {
733                 if (DEBUG) {
734                     Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
735                 }
736                 return;
737             }
738 
739             final int mode = getAppOpMode(vib);
740             if (mode != AppOpsManager.MODE_ALLOWED) {
741                 if (mode == AppOpsManager.MODE_ERRORED) {
742                     // We might be getting calls from within system_server, so we don't actually
743                     // want to throw a SecurityException here.
744                     Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
745                 }
746                 return;
747             }
748             applyVibrationIntensityScalingLocked(vib, intensity);
749             startVibrationInnerLocked(vib);
750         } finally {
751             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
752         }
753     }
754 
755     @GuardedBy("mLock")
startVibrationInnerLocked(Vibration vib)756     private void startVibrationInnerLocked(Vibration vib) {
757         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
758         try {
759             mCurrentVibration = vib;
760             if (vib.effect instanceof VibrationEffect.OneShot) {
761                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
762                 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
763                 doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
764                 mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
765             } else if (vib.effect instanceof VibrationEffect.Waveform) {
766                 // mThread better be null here. doCancelVibrate should always be
767                 // called before startNextVibrationLocked or startVibrationLocked.
768                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
769                 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
770                 mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
771                 mThread.start();
772             } else if (vib.effect instanceof VibrationEffect.Prebaked) {
773                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
774                 long timeout = doVibratorPrebakedEffectLocked(vib);
775                 if (timeout > 0) {
776                     mH.postDelayed(mVibrationEndRunnable, timeout);
777                 }
778             } else {
779                 Slog.e(TAG, "Unknown vibration type, ignoring");
780             }
781         } finally {
782             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
783         }
784     }
785 
isAllowedToVibrateLocked(Vibration vib)786     private boolean isAllowedToVibrateLocked(Vibration vib) {
787         if (!mLowPowerMode) {
788             return true;
789         }
790 
791         if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
792             return true;
793         }
794 
795         if (vib.usageHint == AudioAttributes.USAGE_ALARM ||
796                 vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
797                 vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
798             return true;
799         }
800 
801         return false;
802     }
803 
getCurrentIntensityLocked(Vibration vib)804     private int getCurrentIntensityLocked(Vibration vib) {
805         if (vib.isRingtone()) {
806             return mRingIntensity;
807         } else if (vib.isNotification()) {
808             return mNotificationIntensity;
809         } else if (vib.isHapticFeedback()) {
810             return mHapticFeedbackIntensity;
811         } else if (vib.isAlarm()) {
812             return Vibrator.VIBRATION_INTENSITY_HIGH;
813         } else {
814             return Vibrator.VIBRATION_INTENSITY_MEDIUM;
815         }
816     }
817 
818     /**
819      * Scale the vibration effect by the intensity as appropriate based its intent.
820      */
applyVibrationIntensityScalingLocked(Vibration vib, int intensity)821     private void applyVibrationIntensityScalingLocked(Vibration vib, int intensity) {
822         if (vib.effect instanceof VibrationEffect.Prebaked) {
823             // Prebaked effects are always just a direct translation from intensity to
824             // EffectStrength.
825             VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked)vib.effect;
826             prebaked.setEffectStrength(intensityToEffectStrength(intensity));
827             return;
828         }
829 
830         final int defaultIntensity;
831         if (vib.isRingtone()) {
832             defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
833         } else if (vib.isNotification()) {
834             defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
835         } else if (vib.isHapticFeedback()) {
836             defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
837         } else if (vib.isAlarm()) {
838             defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
839         } else {
840             // If we don't know what kind of vibration we're playing then just skip scaling for
841             // now.
842             return;
843         }
844 
845         final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity);
846         if (scale == null) {
847             // We should have scaling levels for all cases, so not being able to scale because of a
848             // missing level is unexpected.
849             Slog.e(TAG, "No configured scaling level!"
850                     + " (current=" + intensity + ", default= " + defaultIntensity + ")");
851             return;
852         }
853 
854         VibrationEffect scaledEffect = null;
855         if (vib.effect instanceof VibrationEffect.OneShot) {
856             VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
857             oneShot = oneShot.resolve(mDefaultVibrationAmplitude);
858             scaledEffect = oneShot.scale(scale.gamma, scale.maxAmplitude);
859         } else if (vib.effect instanceof VibrationEffect.Waveform) {
860             VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
861             waveform = waveform.resolve(mDefaultVibrationAmplitude);
862             scaledEffect = waveform.scale(scale.gamma, scale.maxAmplitude);
863         } else {
864             Slog.w(TAG, "Unable to apply intensity scaling, unknown VibrationEffect type");
865         }
866 
867         if (scaledEffect != null) {
868             vib.originalEffect = vib.effect;
869             vib.effect = scaledEffect;
870         }
871     }
872 
shouldVibrateForRingtone()873     private boolean shouldVibrateForRingtone() {
874         AudioManager audioManager = mContext.getSystemService(AudioManager.class);
875         int ringerMode = audioManager.getRingerModeInternal();
876         // "Also vibrate for calls" Setting in Sound
877         if (Settings.System.getInt(
878                 mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) {
879             return ringerMode != AudioManager.RINGER_MODE_SILENT;
880         } else if (Settings.Global.getInt(
881                     mContext.getContentResolver(), Settings.Global.APPLY_RAMPING_RINGER, 0) != 0
882                 && DeviceConfig.getBoolean(
883                     DeviceConfig.NAMESPACE_TELEPHONY, RAMPING_RINGER_ENABLED, false)) {
884             return ringerMode != AudioManager.RINGER_MODE_SILENT;
885         } else {
886             return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
887         }
888     }
889 
getAppOpMode(Vibration vib)890     private int getAppOpMode(Vibration vib) {
891         int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
892                 vib.usageHint, vib.uid, vib.opPkg);
893         if (mode == AppOpsManager.MODE_ALLOWED) {
894             mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
895         }
896         return mode;
897     }
898 
899     @GuardedBy("mLock")
reportFinishVibrationLocked()900     private void reportFinishVibrationLocked() {
901         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
902         try {
903             if (mCurrentVibration != null) {
904                 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
905                         mCurrentVibration.opPkg);
906                 unlinkVibration(mCurrentVibration);
907                 mCurrentVibration = null;
908             }
909         } finally {
910             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
911         }
912     }
913 
linkVibration(Vibration vib)914     private void linkVibration(Vibration vib) {
915         // Only link against waveforms since they potentially don't have a finish if
916         // they're repeating. Let other effects just play out until they're done.
917         if (vib.effect instanceof VibrationEffect.Waveform) {
918             try {
919                 vib.token.linkToDeath(vib, 0);
920             } catch (RemoteException e) {
921                 return;
922             }
923         }
924     }
925 
unlinkVibration(Vibration vib)926     private void unlinkVibration(Vibration vib) {
927         if (vib.effect instanceof VibrationEffect.Waveform) {
928             vib.token.unlinkToDeath(vib, 0);
929         }
930     }
931 
updateVibrators()932     private void updateVibrators() {
933         synchronized (mLock) {
934             boolean devicesUpdated = updateInputDeviceVibratorsLocked();
935             boolean lowPowerModeUpdated = updateLowPowerModeLocked();
936             updateVibrationIntensityLocked();
937 
938             if (devicesUpdated || lowPowerModeUpdated) {
939                 // If the state changes out from under us then just reset.
940                 doCancelVibrateLocked();
941             }
942         }
943     }
944 
updateInputDeviceVibratorsLocked()945     private boolean updateInputDeviceVibratorsLocked() {
946         boolean changed = false;
947         boolean vibrateInputDevices = false;
948         try {
949             vibrateInputDevices = Settings.System.getIntForUser(
950                     mContext.getContentResolver(),
951                     Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
952         } catch (SettingNotFoundException snfe) {
953         }
954         if (vibrateInputDevices != mVibrateInputDevicesSetting) {
955             changed = true;
956             mVibrateInputDevicesSetting = vibrateInputDevices;
957         }
958 
959         if (mVibrateInputDevicesSetting) {
960             if (!mInputDeviceListenerRegistered) {
961                 mInputDeviceListenerRegistered = true;
962                 mIm.registerInputDeviceListener(this, mH);
963             }
964         } else {
965             if (mInputDeviceListenerRegistered) {
966                 mInputDeviceListenerRegistered = false;
967                 mIm.unregisterInputDeviceListener(this);
968             }
969         }
970 
971         mInputDeviceVibrators.clear();
972         if (mVibrateInputDevicesSetting) {
973             int[] ids = mIm.getInputDeviceIds();
974             for (int i = 0; i < ids.length; i++) {
975                 InputDevice device = mIm.getInputDevice(ids[i]);
976                 Vibrator vibrator = device.getVibrator();
977                 if (vibrator.hasVibrator()) {
978                     mInputDeviceVibrators.add(vibrator);
979                 }
980             }
981             return true;
982         }
983         return changed;
984     }
985 
updateLowPowerModeLocked()986     private boolean updateLowPowerModeLocked() {
987         boolean lowPowerMode = mPowerManagerInternal
988                 .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
989         if (lowPowerMode != mLowPowerMode) {
990             mLowPowerMode = lowPowerMode;
991             return true;
992         }
993         return false;
994     }
995 
updateVibrationIntensityLocked()996     private void updateVibrationIntensityLocked() {
997         mHapticFeedbackIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
998                 Settings.System.HAPTIC_FEEDBACK_INTENSITY,
999                 mVibrator.getDefaultHapticFeedbackIntensity(), UserHandle.USER_CURRENT);
1000         mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
1001                 Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
1002                 mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT);
1003         mRingIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
1004                 Settings.System.RING_VIBRATION_INTENSITY,
1005                 mVibrator.getDefaultRingVibrationIntensity(), UserHandle.USER_CURRENT);
1006     }
1007 
1008     @Override
onInputDeviceAdded(int deviceId)1009     public void onInputDeviceAdded(int deviceId) {
1010         updateVibrators();
1011     }
1012 
1013     @Override
onInputDeviceChanged(int deviceId)1014     public void onInputDeviceChanged(int deviceId) {
1015         updateVibrators();
1016     }
1017 
1018     @Override
onInputDeviceRemoved(int deviceId)1019     public void onInputDeviceRemoved(int deviceId) {
1020         updateVibrators();
1021     }
1022 
doVibratorExists()1023     private boolean doVibratorExists() {
1024         // For now, we choose to ignore the presence of input devices that have vibrators
1025         // when reporting whether the device has a vibrator.  Applications often use this
1026         // information to decide whether to enable certain features so they expect the
1027         // result of hasVibrator() to be constant.  For now, just report whether
1028         // the device has a built-in vibrator.
1029         //synchronized (mInputDeviceVibrators) {
1030         //    return !mInputDeviceVibrators.isEmpty() || vibratorExists();
1031         //}
1032         return vibratorExists();
1033     }
1034 
doVibratorOn(long millis, int amplitude, int uid, int usageHint)1035     private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
1036         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
1037         try {
1038             synchronized (mInputDeviceVibrators) {
1039                 if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
1040                     amplitude = mDefaultVibrationAmplitude;
1041                 }
1042                 if (DEBUG) {
1043                     Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
1044                             " with amplitude " + amplitude + ".");
1045                 }
1046                 noteVibratorOnLocked(uid, millis);
1047                 final int vibratorCount = mInputDeviceVibrators.size();
1048                 if (vibratorCount != 0) {
1049                     final AudioAttributes attributes =
1050                             new AudioAttributes.Builder().setUsage(usageHint).build();
1051                     for (int i = 0; i < vibratorCount; i++) {
1052                         mInputDeviceVibrators.get(i).vibrate(millis, attributes);
1053                     }
1054                 } else {
1055                     // Note: ordering is important here! Many haptic drivers will reset their
1056                     // amplitude when enabled, so we always have to enable frst, then set the
1057                     // amplitude.
1058                     vibratorOn(millis);
1059                     doVibratorSetAmplitude(amplitude);
1060                 }
1061             }
1062         } finally {
1063             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1064         }
1065     }
1066 
doVibratorSetAmplitude(int amplitude)1067     private void doVibratorSetAmplitude(int amplitude) {
1068         if (mSupportsAmplitudeControl) {
1069             vibratorSetAmplitude(amplitude);
1070         }
1071     }
1072 
doVibratorOff()1073     private void doVibratorOff() {
1074         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff");
1075         try {
1076             synchronized (mInputDeviceVibrators) {
1077                 if (DEBUG) {
1078                     Slog.d(TAG, "Turning vibrator off.");
1079                 }
1080                 noteVibratorOffLocked();
1081                 final int vibratorCount = mInputDeviceVibrators.size();
1082                 if (vibratorCount != 0) {
1083                     for (int i = 0; i < vibratorCount; i++) {
1084                         mInputDeviceVibrators.get(i).cancel();
1085                     }
1086                 } else {
1087                     vibratorOff();
1088                 }
1089             }
1090         } finally {
1091             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1092         }
1093     }
1094 
1095     @GuardedBy("mLock")
doVibratorPrebakedEffectLocked(Vibration vib)1096     private long doVibratorPrebakedEffectLocked(Vibration vib) {
1097         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked");
1098         try {
1099             final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
1100             final boolean usingInputDeviceVibrators;
1101             synchronized (mInputDeviceVibrators) {
1102                 usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
1103             }
1104             // Input devices don't support prebaked effect, so skip trying it with them.
1105             if (!usingInputDeviceVibrators) {
1106                 long timeout = vibratorPerformEffect(prebaked.getId(),
1107                         prebaked.getEffectStrength());
1108                 if (timeout > 0) {
1109                     noteVibratorOnLocked(vib.uid, timeout);
1110                     return timeout;
1111                 }
1112             }
1113             if (!prebaked.shouldFallback()) {
1114                 return 0;
1115             }
1116             VibrationEffect effect = getFallbackEffect(prebaked.getId());
1117             if (effect == null) {
1118                 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
1119                 return 0;
1120             }
1121             Vibration fallbackVib = new Vibration(vib.token, effect, vib.usageHint, vib.uid,
1122                     vib.opPkg, vib.reason + " (fallback)");
1123             final int intensity = getCurrentIntensityLocked(fallbackVib);
1124             linkVibration(fallbackVib);
1125             applyVibrationIntensityScalingLocked(fallbackVib, intensity);
1126             startVibrationInnerLocked(fallbackVib);
1127             return 0;
1128         } finally {
1129             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1130         }
1131     }
1132 
getFallbackEffect(int effectId)1133     private VibrationEffect getFallbackEffect(int effectId) {
1134         return mFallbackEffects.get(effectId);
1135     }
1136 
1137     /**
1138      * Return the current desired effect strength.
1139      *
1140      * If the returned value is &lt; 0 then the vibration shouldn't be played at all.
1141      */
intensityToEffectStrength(int intensity)1142     private static int intensityToEffectStrength(int intensity) {
1143         switch (intensity) {
1144             case Vibrator.VIBRATION_INTENSITY_LOW:
1145                 return EffectStrength.LIGHT;
1146             case Vibrator.VIBRATION_INTENSITY_MEDIUM:
1147                 return EffectStrength.MEDIUM;
1148             case Vibrator.VIBRATION_INTENSITY_HIGH:
1149                 return EffectStrength.STRONG;
1150             default:
1151                 Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
1152                 return EffectStrength.STRONG;
1153         }
1154     }
1155 
isNotification(int usageHint)1156     private static boolean isNotification(int usageHint) {
1157         switch (usageHint) {
1158             case AudioAttributes.USAGE_NOTIFICATION:
1159             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
1160             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
1161             case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
1162                 return true;
1163             default:
1164                 return false;
1165         }
1166     }
1167 
isRingtone(int usageHint)1168     private static boolean isRingtone(int usageHint) {
1169         return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
1170     }
1171 
isHapticFeedback(int usageHint)1172     private static boolean isHapticFeedback(int usageHint) {
1173         return usageHint == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
1174     }
1175 
isAlarm(int usageHint)1176     private static boolean isAlarm(int usageHint) {
1177         return usageHint == AudioAttributes.USAGE_ALARM;
1178     }
1179 
noteVibratorOnLocked(int uid, long millis)1180     private void noteVibratorOnLocked(int uid, long millis) {
1181         try {
1182             mBatteryStatsService.noteVibratorOn(uid, millis);
1183             StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, uid, null,
1184                     StatsLog.VIBRATOR_STATE_CHANGED__STATE__ON, millis);
1185             mCurVibUid = uid;
1186         } catch (RemoteException e) {
1187         }
1188     }
1189 
noteVibratorOffLocked()1190     private void noteVibratorOffLocked() {
1191         if (mCurVibUid >= 0) {
1192             try {
1193                 mBatteryStatsService.noteVibratorOff(mCurVibUid);
1194                 StatsLog.write_non_chained(StatsLog.VIBRATOR_STATE_CHANGED, mCurVibUid, null,
1195                         StatsLog.VIBRATOR_STATE_CHANGED__STATE__OFF, 0);
1196             } catch (RemoteException e) { }
1197             mCurVibUid = -1;
1198         }
1199     }
1200 
setVibratorUnderExternalControl(boolean externalControl)1201     private void setVibratorUnderExternalControl(boolean externalControl) {
1202         if (DEBUG) {
1203             if (externalControl) {
1204                 Slog.d(TAG, "Vibrator going under external control.");
1205             } else {
1206                 Slog.d(TAG, "Taking back control of vibrator.");
1207             }
1208         }
1209         mVibratorUnderExternalControl = externalControl;
1210         vibratorSetExternalControl(externalControl);
1211     }
1212 
1213     private class VibrateThread extends Thread {
1214         private final VibrationEffect.Waveform mWaveform;
1215         private final int mUid;
1216         private final int mUsageHint;
1217 
1218         private boolean mForceStop;
1219 
VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint)1220         VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
1221             mWaveform = waveform;
1222             mUid = uid;
1223             mUsageHint = usageHint;
1224             mTmpWorkSource.set(uid);
1225             mWakeLock.setWorkSource(mTmpWorkSource);
1226         }
1227 
delayLocked(long duration)1228         private long delayLocked(long duration) {
1229             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "delayLocked");
1230             try {
1231                 long durationRemaining = duration;
1232                 if (duration > 0) {
1233                     final long bedtime = duration + SystemClock.uptimeMillis();
1234                     do {
1235                         try {
1236                             this.wait(durationRemaining);
1237                         }
1238                         catch (InterruptedException e) { }
1239                         if (mForceStop) {
1240                             break;
1241                         }
1242                         durationRemaining = bedtime - SystemClock.uptimeMillis();
1243                     } while (durationRemaining > 0);
1244                     return duration - durationRemaining;
1245                 }
1246                 return 0;
1247             } finally {
1248                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1249             }
1250         }
1251 
run()1252         public void run() {
1253             Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
1254             mWakeLock.acquire();
1255             try {
1256                 boolean finished = playWaveform();
1257                 if (finished) {
1258                     onVibrationFinished();
1259                 }
1260             } finally {
1261                 mWakeLock.release();
1262             }
1263         }
1264 
1265         /**
1266          * Play the waveform.
1267          *
1268          * @return true if it finished naturally, false otherwise (e.g. it was canceled).
1269          */
playWaveform()1270         public boolean playWaveform() {
1271             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playWaveform");
1272             try {
1273                 synchronized (this) {
1274                     final long[] timings = mWaveform.getTimings();
1275                     final int[] amplitudes = mWaveform.getAmplitudes();
1276                     final int len = timings.length;
1277                     final int repeat = mWaveform.getRepeatIndex();
1278 
1279                     int index = 0;
1280                     long onDuration = 0;
1281                     while (!mForceStop) {
1282                         if (index < len) {
1283                             final int amplitude = amplitudes[index];
1284                             final long duration = timings[index++];
1285                             if (duration <= 0) {
1286                                 continue;
1287                             }
1288                             if (amplitude != 0) {
1289                                 if (onDuration <= 0) {
1290                                     // Telling the vibrator to start multiple times usually causes
1291                                     // effects to feel "choppy" because the motor resets at every on
1292                                     // command.  Instead we figure out how long our next "on" period
1293                                     // is going to be, tell the motor to stay on for the full
1294                                     // duration, and then wake up to change the amplitude at the
1295                                     // appropriate intervals.
1296                                     onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
1297                                             repeat);
1298                                     doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
1299                                 } else {
1300                                     doVibratorSetAmplitude(amplitude);
1301                                 }
1302                             }
1303 
1304                             long waitTime = delayLocked(duration);
1305                             if (amplitude != 0) {
1306                                 onDuration -= waitTime;
1307                             }
1308                         } else if (repeat < 0) {
1309                             break;
1310                         } else {
1311                             index = repeat;
1312                         }
1313                     }
1314                     return !mForceStop;
1315                 }
1316             } finally {
1317                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1318             }
1319         }
1320 
cancel()1321         public void cancel() {
1322             synchronized (this) {
1323                 mThread.mForceStop = true;
1324                 mThread.notify();
1325             }
1326         }
1327 
1328         /**
1329          * Get the duration the vibrator will be on starting at startIndex until the next time it's
1330          * off.
1331          */
getTotalOnDuration( long[] timings, int[] amplitudes, int startIndex, int repeatIndex)1332         private long getTotalOnDuration(
1333                 long[] timings, int[] amplitudes, int startIndex, int repeatIndex) {
1334             int i = startIndex;
1335             long timing = 0;
1336             while(amplitudes[i] != 0) {
1337                 timing += timings[i++];
1338                 if (i >= timings.length) {
1339                     if (repeatIndex >= 0) {
1340                         i = repeatIndex;
1341                         // prevent infinite loop
1342                         repeatIndex = -1;
1343                     } else {
1344                         break;
1345                     }
1346                 }
1347                 if (i == startIndex) {
1348                     return 1000;
1349                 }
1350             }
1351             return timing;
1352         }
1353     }
1354 
1355     BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
1356         @Override
1357         public void onReceive(Context context, Intent intent) {
1358             if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
1359                 synchronized (mLock) {
1360                     // When the system is entering a non-interactive state, we want
1361                     // to cancel vibrations in case a misbehaving app has abandoned
1362                     // them.  However it may happen that the system is currently playing
1363                     // haptic feedback as part of the transition.  So we don't cancel
1364                     // system vibrations.
1365                     if (mCurrentVibration != null
1366                             && !(mCurrentVibration.isHapticFeedback()
1367                                 && mCurrentVibration.isFromSystem())) {
1368                         doCancelVibrateLocked();
1369                     }
1370                 }
1371             }
1372         }
1373     };
1374 
1375     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)1376     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1377         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
1378 
1379         pw.println("Vibrator Service:");
1380         synchronized (mLock) {
1381             pw.print("  mCurrentVibration=");
1382             if (mCurrentVibration != null) {
1383                 pw.println(mCurrentVibration.toInfo().toString());
1384             } else {
1385                 pw.println("null");
1386             }
1387             pw.print("  mCurrentExternalVibration=");
1388             if (mCurrentExternalVibration != null) {
1389                 pw.println(mCurrentExternalVibration.toString());
1390             } else {
1391                 pw.println("null");
1392             }
1393             pw.println("  mVibratorUnderExternalControl=" + mVibratorUnderExternalControl);
1394             pw.println("  mLowPowerMode=" + mLowPowerMode);
1395             pw.println("  mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
1396             pw.println("  mNotificationIntensity=" + mNotificationIntensity);
1397             pw.println("  mRingIntensity=" + mRingIntensity);
1398             pw.println("");
1399             pw.println("  Previous ring vibrations:");
1400             for (VibrationInfo info : mPreviousRingVibrations) {
1401                 pw.print("    ");
1402                 pw.println(info.toString());
1403             }
1404 
1405             pw.println("  Previous notification vibrations:");
1406             for (VibrationInfo info : mPreviousNotificationVibrations) {
1407                 pw.print("    ");
1408                 pw.println(info.toString());
1409             }
1410 
1411             pw.println("  Previous alarm vibrations:");
1412             for (VibrationInfo info : mPreviousAlarmVibrations) {
1413                 pw.print("    ");
1414                 pw.println(info.toString());
1415             }
1416 
1417             pw.println("  Previous vibrations:");
1418             for (VibrationInfo info : mPreviousVibrations) {
1419                 pw.print("    ");
1420                 pw.println(info.toString());
1421             }
1422 
1423             pw.println("  Previous external vibrations:");
1424             for (ExternalVibration vib : mPreviousExternalVibrations) {
1425                 pw.print("    ");
1426                 pw.println(vib.toString());
1427             }
1428         }
1429     }
1430 
1431     @Override
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)1432     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
1433             String[] args, ShellCallback callback, ResultReceiver resultReceiver)
1434             throws RemoteException {
1435         new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
1436     }
1437 
1438     final class ExternalVibratorService extends IExternalVibratorService.Stub {
1439         ExternalVibrationDeathRecipient mCurrentExternalDeathRecipient;
1440 
1441         @Override
onExternalVibrationStart(ExternalVibration vib)1442         public int onExternalVibrationStart(ExternalVibration vib) {
1443             if (!mSupportsExternalControl) {
1444                 return SCALE_MUTE;
1445             }
1446             if (ActivityManager.checkComponentPermission(android.Manifest.permission.VIBRATE,
1447                         vib.getUid(), -1 /*owningUid*/, true /*exported*/)
1448                     != PackageManager.PERMISSION_GRANTED) {
1449                 Slog.w(TAG, "pkg=" + vib.getPackage() + ", uid=" + vib.getUid()
1450                         + " tried to play externally controlled vibration"
1451                         + " without VIBRATE permission, ignoring.");
1452                 return SCALE_MUTE;
1453             }
1454 
1455             final int scaleLevel;
1456             synchronized (mLock) {
1457                 if (!vib.equals(mCurrentExternalVibration)) {
1458                     if (mCurrentExternalVibration == null) {
1459                         // If we're not under external control right now, then cancel any normal
1460                         // vibration that may be playing and ready the vibrator for external
1461                         // control.
1462                         doCancelVibrateLocked();
1463                         setVibratorUnderExternalControl(true);
1464                     }
1465                     // At this point we either have an externally controlled vibration playing, or
1466                     // no vibration playing. Since the interface defines that only one externally
1467                     // controlled vibration can play at a time, by returning something other than
1468                     // SCALE_MUTE from this function we can be assured that if we are currently
1469                     // playing vibration, it will be muted in favor of the new vibration.
1470                     //
1471                     // Note that this doesn't support multiple concurrent external controls, as we
1472                     // would need to mute the old one still if it came from a different controller.
1473                     mCurrentExternalVibration = vib;
1474                     mCurrentExternalDeathRecipient = new ExternalVibrationDeathRecipient();
1475                     mCurrentExternalVibration.linkToDeath(mCurrentExternalDeathRecipient);
1476                     if (mPreviousExternalVibrations.size() > mPreviousVibrationsLimit) {
1477                         mPreviousExternalVibrations.removeFirst();
1478                     }
1479                     mPreviousExternalVibrations.addLast(vib);
1480                     if (DEBUG) {
1481                         Slog.e(TAG, "Playing external vibration: " + vib);
1482                     }
1483                 }
1484                 final int usage = vib.getAudioAttributes().getUsage();
1485                 final int defaultIntensity;
1486                 final int currentIntensity;
1487                 if (isRingtone(usage)) {
1488                     defaultIntensity = mVibrator.getDefaultRingVibrationIntensity();
1489                     currentIntensity = mRingIntensity;
1490                 } else if (isNotification(usage)) {
1491                     defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
1492                     currentIntensity = mNotificationIntensity;
1493                 } else if (isHapticFeedback(usage)) {
1494                     defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
1495                     currentIntensity = mHapticFeedbackIntensity;
1496                 } else if (isAlarm(usage)) {
1497                     defaultIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
1498                     currentIntensity = Vibrator.VIBRATION_INTENSITY_HIGH;
1499                 } else {
1500                     defaultIntensity = 0;
1501                     currentIntensity = 0;
1502                 }
1503                 scaleLevel = currentIntensity - defaultIntensity;
1504             }
1505             if (scaleLevel >= SCALE_VERY_LOW && scaleLevel <= SCALE_VERY_HIGH) {
1506                 return scaleLevel;
1507             } else {
1508                 // Presumably we want to play this but something about our scaling has gone
1509                 // wrong, so just play with no scaling.
1510                 Slog.w(TAG, "Error in scaling calculations, ended up with invalid scale level "
1511                         + scaleLevel + " for vibration " + vib);
1512                 return SCALE_NONE;
1513             }
1514         }
1515 
1516         @Override
onExternalVibrationStop(ExternalVibration vib)1517         public void onExternalVibrationStop(ExternalVibration vib) {
1518             synchronized (mLock) {
1519                 if (vib.equals(mCurrentExternalVibration)) {
1520                     mCurrentExternalVibration.unlinkToDeath(mCurrentExternalDeathRecipient);
1521                     mCurrentExternalDeathRecipient = null;
1522                     mCurrentExternalVibration = null;
1523                     setVibratorUnderExternalControl(false);
1524                     if (DEBUG) {
1525                         Slog.e(TAG, "Stopping external vibration" + vib);
1526                     }
1527                 }
1528             }
1529         }
1530 
1531         private class ExternalVibrationDeathRecipient implements IBinder.DeathRecipient {
binderDied()1532             public void binderDied() {
1533                 synchronized (mLock) {
1534                     onExternalVibrationStop(mCurrentExternalVibration);
1535                 }
1536             }
1537         }
1538     }
1539 
1540     private final class VibratorShellCommand extends ShellCommand {
1541 
1542         private final IBinder mToken;
1543 
1544         private final class CommonOptions {
1545             public boolean force = false;
check(String opt)1546             public void check(String opt) {
1547                 switch (opt) {
1548                     case "-f":
1549                         force = true;
1550                         break;
1551                 }
1552             }
1553         }
1554 
VibratorShellCommand(IBinder token)1555         private VibratorShellCommand(IBinder token) {
1556             mToken = token;
1557         }
1558 
1559         @Override
onCommand(String cmd)1560         public int onCommand(String cmd) {
1561             if ("vibrate".equals(cmd)) {
1562                 return runVibrate();
1563             } else if ("waveform".equals(cmd)) {
1564                 return runWaveform();
1565             } else if ("prebaked".equals(cmd)) {
1566                 return runPrebaked();
1567             } else if ("cancel".equals(cmd)) {
1568                 cancelVibrate(mToken);
1569                 return 0;
1570             }
1571             return handleDefaultCommands(cmd);
1572         }
1573 
checkDoNotDisturb(CommonOptions opts)1574         private boolean checkDoNotDisturb(CommonOptions opts) {
1575             try {
1576                 final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
1577                         Settings.Global.ZEN_MODE);
1578                 if (zenMode != Settings.Global.ZEN_MODE_OFF && !opts.force) {
1579                     try (PrintWriter pw = getOutPrintWriter();) {
1580                         pw.print("Ignoring because device is on DND mode ");
1581                         pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
1582                                 zenMode));
1583                         return true;
1584                     }
1585                 }
1586             } catch (SettingNotFoundException e) {
1587                 // ignore
1588             }
1589 
1590             return false;
1591         }
1592 
runVibrate()1593         private int runVibrate() {
1594             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
1595             try {
1596                 CommonOptions commonOptions = new CommonOptions();
1597 
1598                 String opt;
1599                 while ((opt = getNextOption()) != null) {
1600                     commonOptions.check(opt);
1601                 }
1602 
1603                 if (checkDoNotDisturb(commonOptions)) {
1604                     return 0;
1605                 }
1606 
1607                 final long duration = Long.parseLong(getNextArgRequired());
1608                 String description = getNextArg();
1609                 if (description == null) {
1610                     description = "Shell command";
1611                 }
1612 
1613                 VibrationEffect effect =
1614                         VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
1615                 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
1616                         "Shell Command", mToken);
1617                 return 0;
1618             } finally {
1619                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1620             }
1621         }
1622 
runWaveform()1623         private int runWaveform() {
1624             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runWaveform");
1625             try {
1626                 String description = "Shell command";
1627                 int repeat = -1;
1628                 ArrayList<Integer> amplitudesList = null;
1629                 CommonOptions commonOptions = new CommonOptions();
1630 
1631                 String opt;
1632                 while ((opt = getNextOption()) != null) {
1633                     switch (opt) {
1634                         case "-d":
1635                             description = getNextArgRequired();
1636                             break;
1637                         case "-r":
1638                             repeat = Integer.parseInt(getNextArgRequired());
1639                             break;
1640                         case "-a":
1641                             if (amplitudesList == null) {
1642                                 amplitudesList = new ArrayList<Integer>();
1643                             }
1644                             break;
1645                         default:
1646                             commonOptions.check(opt);
1647                             break;
1648                     }
1649                 }
1650 
1651                 if (checkDoNotDisturb(commonOptions)) {
1652                     return 0;
1653                 }
1654 
1655                 ArrayList<Long> timingsList = new ArrayList<Long>();
1656 
1657                 String arg;
1658                 while ((arg = getNextArg()) != null) {
1659                     if (amplitudesList != null && amplitudesList.size() < timingsList.size()) {
1660                         amplitudesList.add(Integer.parseInt(arg));
1661                     } else {
1662                         timingsList.add(Long.parseLong(arg));
1663                     }
1664                 }
1665 
1666                 VibrationEffect effect;
1667                 long[] timings = timingsList.stream().mapToLong(Long::longValue).toArray();
1668                 if (amplitudesList == null) {
1669                     effect = VibrationEffect.createWaveform(timings, repeat);
1670                 } else {
1671                     int[] amplitudes =
1672                             amplitudesList.stream().mapToInt(Integer::intValue).toArray();
1673                     effect = VibrationEffect.createWaveform(timings, amplitudes, repeat);
1674                 }
1675                 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
1676                         "Shell Command", mToken);
1677                 return 0;
1678             } finally {
1679                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1680             }
1681         }
1682 
runPrebaked()1683         private int runPrebaked() {
1684             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runPrebaked");
1685             try {
1686                 CommonOptions commonOptions = new CommonOptions();
1687 
1688                 String opt;
1689                 while ((opt = getNextOption()) != null) {
1690                     commonOptions.check(opt);
1691                 }
1692 
1693                 if (checkDoNotDisturb(commonOptions)) {
1694                     return 0;
1695                 }
1696 
1697                 final int id = Integer.parseInt(getNextArgRequired());
1698 
1699                 String description = getNextArg();
1700                 if (description == null) {
1701                     description = "Shell command";
1702                 }
1703 
1704                 VibrationEffect effect =
1705                         VibrationEffect.get(id, false);
1706                 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
1707                         "Shell Command", mToken);
1708                 return 0;
1709             } finally {
1710                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
1711             }
1712         }
1713 
1714         @Override
onHelp()1715         public void onHelp() {
1716             try (PrintWriter pw = getOutPrintWriter();) {
1717                 pw.println("Vibrator commands:");
1718                 pw.println("  help");
1719                 pw.println("    Prints this help text.");
1720                 pw.println("");
1721                 pw.println("  vibrate duration [description]");
1722                 pw.println("    Vibrates for duration milliseconds; ignored when device is on DND ");
1723                 pw.println("    (Do Not Disturb) mode.");
1724                 pw.println("  waveform [-d description] [-r index] [-a] duration [amplitude] ...");
1725                 pw.println("    Vibrates for durations and amplitudes in list;");
1726                 pw.println("    ignored when device is on DND (Do Not Disturb) mode.");
1727                 pw.println("    If -r is provided, the waveform loops back to the specified");
1728                 pw.println("    index (e.g. 0 loops from the beginning)");
1729                 pw.println("    If -a is provided, the command accepts duration-amplitude pairs;");
1730                 pw.println("    otherwise, it accepts durations only and alternates off/on");
1731                 pw.println("    Duration is in milliseconds; amplitude is a scale of 1-255.");
1732                 pw.println("  prebaked effect-id [description]");
1733                 pw.println("    Vibrates with prebaked effect; ignored when device is on DND ");
1734                 pw.println("    (Do Not Disturb) mode.");
1735                 pw.println("  cancel");
1736                 pw.println("    Cancels any active vibration");
1737                 pw.println("Common Options:");
1738                 pw.println("  -f - Force. Ignore Do Not Disturb setting.");
1739                 pw.println("");
1740             }
1741         }
1742     }
1743 
1744 }
1745