Home | History | Annotate | Download | only in server
      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 
    159     static native boolean vibratorExists();
    160     static native void vibratorInit();
    161     static native void vibratorOn(long milliseconds);
    162     static native void vibratorOff();
    163     static native boolean vibratorSupportsAmplitudeControl();
    164     static native void vibratorSetAmplitude(int amplitude);
    165     static native long vibratorPerformEffect(long effect, long strength);
    166     static native boolean vibratorSupportsExternalControl();
    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 
    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 
    220         public void binderDied() {
    221             synchronized (mLock) {
    222                 if (this == mCurrentVibration) {
    223                     doCancelVibrateLocked();
    224                 }
    225             }
    226         }
    227 
    228         public boolean hasTimeoutLongerThan(long millis) {
    229             final long duration = effect.getDuration();
    230             return duration >= 0 && duration > millis;
    231         }
    232 
    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 
    258         public boolean isNotification() {
    259             return VibratorService.this.isNotification(usageHint);
    260         }
    261 
    262         public boolean isRingtone() {
    263             return VibratorService.this.isRingtone(usageHint);
    264         }
    265 
    266         public boolean isAlarm() {
    267             return VibratorService.this.isAlarm(usageHint);
    268         }
    269 
    270         public boolean isFromSystem() {
    271             return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
    272         }
    273 
    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 
    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
    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 
    326         public ScaleLevel(float gamma) {
    327             this(gamma, VibrationEffect.MAX_AMPLITUDE);
    328         }
    329 
    330         public ScaleLevel(float gamma, int maxAmplitude) {
    331             this.gamma = gamma;
    332             this.maxAmplitude = maxAmplitude;
    333         }
    334 
    335         @Override
    336         public String toString() {
    337             return "ScaleLevel{gamma=" + gamma + ", maxAmplitude=" + maxAmplitude + "}";
    338         }
    339     }
    340 
    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 
    407     private VibrationEffect createEffectFromResource(int resId) {
    408         long[] timings = getLongIntArray(mContext.getResources(), resId);
    409         return createEffectFromTimings(timings);
    410     }
    411 
    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 
    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 {
    481         public SettingsObserver(Handler handler) {
    482             super(handler);
    483         }
    484 
    485         @Override
    486         public void onChange(boolean SelfChange) {
    487             updateVibrators();
    488         }
    489     }
    490 
    491     @Override // Binder call
    492     public boolean hasVibrator() {
    493         return doVibratorExists();
    494     }
    495 
    496     @Override // Binder call
    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 
    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      */
    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 
    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
    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 
    635     private static boolean isRepeatingVibration(VibrationEffect effect) {
    636         return effect.getDuration() == Long.MAX_VALUE;
    637     }
    638 
    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
    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")
    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
    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")
    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")
    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 
    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 
    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      */
    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 
    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 
    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")
    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 
    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 
    926     private void unlinkVibration(Vibration vib) {
    927         if (vib.effect instanceof VibrationEffect.Waveform) {
    928             vib.token.unlinkToDeath(vib, 0);
    929         }
    930     }
    931 
    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 
    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 
    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 
    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
   1009     public void onInputDeviceAdded(int deviceId) {
   1010         updateVibrators();
   1011     }
   1012 
   1013     @Override
   1014     public void onInputDeviceChanged(int deviceId) {
   1015         updateVibrators();
   1016     }
   1017 
   1018     @Override
   1019     public void onInputDeviceRemoved(int deviceId) {
   1020         updateVibrators();
   1021     }
   1022 
   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 
   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 
   1067     private void doVibratorSetAmplitude(int amplitude) {
   1068         if (mSupportsAmplitudeControl) {
   1069             vibratorSetAmplitude(amplitude);
   1070         }
   1071     }
   1072 
   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")
   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 
   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      */
   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 
   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 
   1168     private static boolean isRingtone(int usageHint) {
   1169         return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
   1170     }
   1171 
   1172     private static boolean isHapticFeedback(int usageHint) {
   1173         return usageHint == AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
   1174     }
   1175 
   1176     private static boolean isAlarm(int usageHint) {
   1177         return usageHint == AudioAttributes.USAGE_ALARM;
   1178     }
   1179 
   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 
   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 
   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 
   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 
   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 
   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          */
   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 
   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          */
   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
   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
   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
   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
   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 {
   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;
   1546             public void check(String opt) {
   1547                 switch (opt) {
   1548                     case "-f":
   1549                         force = true;
   1550                         break;
   1551                 }
   1552             }
   1553         }
   1554 
   1555         private VibratorShellCommand(IBinder token) {
   1556             mToken = token;
   1557         }
   1558 
   1559         @Override
   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 
   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 
   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 
   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 
   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
   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