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.AppOpsManager;
     20 import android.content.BroadcastReceiver;
     21 import android.content.Context;
     22 import android.content.Intent;
     23 import android.content.IntentFilter;
     24 import android.content.pm.PackageManager;
     25 import android.content.res.Resources;
     26 import android.database.ContentObserver;
     27 import android.hardware.input.InputManager;
     28 import android.hardware.vibrator.V1_0.EffectStrength;
     29 import android.icu.text.DateFormat;
     30 import android.media.AudioManager;
     31 import android.os.PowerManager.ServiceType;
     32 import android.os.PowerSaveState;
     33 import android.os.BatteryStats;
     34 import android.os.Handler;
     35 import android.os.IVibratorService;
     36 import android.os.PowerManager;
     37 import android.os.PowerManagerInternal;
     38 import android.os.Process;
     39 import android.os.RemoteException;
     40 import android.os.ResultReceiver;
     41 import android.os.IBinder;
     42 import android.os.Binder;
     43 import android.os.ServiceManager;
     44 import android.os.ShellCallback;
     45 import android.os.ShellCommand;
     46 import android.os.SystemClock;
     47 import android.os.Trace;
     48 import android.os.UserHandle;
     49 import android.os.Vibrator;
     50 import android.os.VibrationEffect;
     51 import android.os.WorkSource;
     52 import android.provider.Settings;
     53 import android.provider.Settings.SettingNotFoundException;
     54 import android.util.DebugUtils;
     55 import android.util.Slog;
     56 import android.util.SparseArray;
     57 import android.view.InputDevice;
     58 import android.media.AudioAttributes;
     59 
     60 import com.android.internal.annotations.GuardedBy;
     61 import com.android.internal.app.IAppOpsService;
     62 import com.android.internal.app.IBatteryStats;
     63 import com.android.internal.util.DumpUtils;
     64 
     65 import java.io.FileDescriptor;
     66 import java.io.PrintWriter;
     67 import java.util.ArrayList;
     68 import java.util.LinkedList;
     69 import java.util.Date;
     70 
     71 public class VibratorService extends IVibratorService.Stub
     72         implements InputManager.InputDeviceListener {
     73     private static final String TAG = "VibratorService";
     74     private static final boolean DEBUG = false;
     75     private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
     76 
     77     private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 };
     78 
     79     // Scale levels. Each level is defined as the delta between the current setting and the default
     80     // intensity for that type of vibration (i.e. current - default).
     81     private static final int SCALE_VERY_LOW = -2;
     82     private static final int SCALE_LOW = -1;
     83     private static final int SCALE_NONE = 0;
     84     private static final int SCALE_HIGH = 1;
     85     private static final int SCALE_VERY_HIGH = 2;
     86 
     87     // Gamma adjustments for scale levels.
     88     private static final float SCALE_VERY_LOW_GAMMA = 2.0f;
     89     private static final float SCALE_LOW_GAMMA = 1.5f;
     90     private static final float SCALE_NONE_GAMMA = 1.0f;
     91     private static final float SCALE_HIGH_GAMMA = 0.5f;
     92     private static final float SCALE_VERY_HIGH_GAMMA = 0.25f;
     93 
     94     // Max amplitudes for scale levels. If one is not listed, then the max amplitude is the default
     95     // max amplitude.
     96     private static final int SCALE_VERY_LOW_MAX_AMPLITUDE = 168; // 2/3 * 255
     97     private static final int SCALE_LOW_MAX_AMPLITUDE = 192; // 3/4 * 255
     98 
     99     // If a vibration is playing for longer than 5s, it's probably not haptic feedback.
    100     private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000;
    101 
    102 
    103     // A mapping from the intensity adjustment to the scaling to apply, where the intensity
    104     // adjustment is defined as the delta between the default intensity level and the user selected
    105     // intensity level. It's important that we apply the scaling on the delta between the two so
    106     // that the default intensity level applies no scaling to application provided effects.
    107     private final SparseArray<ScaleLevel> mScaleLevels;
    108     private final LinkedList<VibrationInfo> mPreviousVibrations;
    109     private final int mPreviousVibrationsLimit;
    110     private final boolean mAllowPriorityVibrationsInLowPowerMode;
    111     private final boolean mSupportsAmplitudeControl;
    112     private final int mDefaultVibrationAmplitude;
    113     private final SparseArray<VibrationEffect> mFallbackEffects;
    114     private final WorkSource mTmpWorkSource = new WorkSource();
    115     private final Handler mH = new Handler();
    116     private final Object mLock = new Object();
    117 
    118     private final Context mContext;
    119     private final PowerManager.WakeLock mWakeLock;
    120     private final AppOpsManager mAppOps;
    121     private final IBatteryStats mBatteryStatsService;
    122     private PowerManagerInternal mPowerManagerInternal;
    123     private InputManager mIm;
    124     private Vibrator mVibrator;
    125     private SettingsObserver mSettingObserver;
    126 
    127     private volatile VibrateThread mThread;
    128 
    129     // mInputDeviceVibrators lock should be acquired after mLock, if both are
    130     // to be acquired
    131     private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
    132     private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
    133     private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
    134 
    135     @GuardedBy("mLock")
    136     private Vibration mCurrentVibration;
    137     private int mCurVibUid = -1;
    138     private boolean mLowPowerMode;
    139     private int mHapticFeedbackIntensity;
    140     private int mNotificationIntensity;
    141 
    142     native static boolean vibratorExists();
    143     native static void vibratorInit();
    144     native static void vibratorOn(long milliseconds);
    145     native static void vibratorOff();
    146     native static boolean vibratorSupportsAmplitudeControl();
    147     native static void vibratorSetAmplitude(int amplitude);
    148     native static long vibratorPerformEffect(long effect, long strength);
    149 
    150     private class Vibration implements IBinder.DeathRecipient {
    151         public final IBinder token;
    152         // Start time in CLOCK_BOOTTIME base.
    153         public final long startTime;
    154         // Start time in unix epoch time. Only to be used for debugging purposes and to correlate
    155         // with other system events, any duration calculations should be done use startTime so as
    156         // not to be affected by discontinuities created by RTC adjustments.
    157         public final long startTimeDebug;
    158         public final int usageHint;
    159         public final int uid;
    160         public final String opPkg;
    161 
    162         // The actual effect to be played.
    163         public VibrationEffect effect;
    164         // The original effect that was requested. This is non-null only when the original effect
    165         // differs from the effect that's being played. Typically these two things differ because
    166         // the effect was scaled based on the users vibration intensity settings.
    167         public VibrationEffect originalEffect;
    168 
    169         private Vibration(IBinder token, VibrationEffect effect,
    170                 int usageHint, int uid, String opPkg) {
    171             this.token = token;
    172             this.effect = effect;
    173             this.startTime = SystemClock.elapsedRealtime();
    174             this.startTimeDebug = System.currentTimeMillis();
    175             this.usageHint = usageHint;
    176             this.uid = uid;
    177             this.opPkg = opPkg;
    178         }
    179 
    180         public void binderDied() {
    181             synchronized (mLock) {
    182                 if (this == mCurrentVibration) {
    183                     doCancelVibrateLocked();
    184                 }
    185             }
    186         }
    187 
    188         public boolean hasTimeoutLongerThan(long millis) {
    189             final long duration = effect.getDuration();
    190             return duration >= 0 && duration > millis;
    191         }
    192 
    193         public boolean isHapticFeedback() {
    194             if (effect instanceof VibrationEffect.Prebaked) {
    195                 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect;
    196                 switch (prebaked.getId()) {
    197                     case VibrationEffect.EFFECT_CLICK:
    198                     case VibrationEffect.EFFECT_DOUBLE_CLICK:
    199                     case VibrationEffect.EFFECT_HEAVY_CLICK:
    200                     case VibrationEffect.EFFECT_TICK:
    201                     case VibrationEffect.EFFECT_POP:
    202                     case VibrationEffect.EFFECT_THUD:
    203                         return true;
    204                     default:
    205                         Slog.w(TAG, "Unknown prebaked vibration effect, "
    206                                 + "assuming it isn't haptic feedback.");
    207                         return false;
    208                 }
    209             }
    210             final long duration = effect.getDuration();
    211             return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION;
    212         }
    213 
    214         public boolean isNotification() {
    215             switch (usageHint) {
    216                 case AudioAttributes.USAGE_NOTIFICATION:
    217                 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
    218                 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
    219                 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
    220                     return true;
    221                 default:
    222                     return false;
    223             }
    224         }
    225 
    226         public boolean isRingtone() {
    227             return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
    228         }
    229 
    230         public boolean isFromSystem() {
    231             return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
    232         }
    233 
    234         public VibrationInfo toInfo() {
    235             return new VibrationInfo(
    236                     startTimeDebug, effect, originalEffect, usageHint, uid, opPkg);
    237         }
    238     }
    239 
    240     private static class VibrationInfo {
    241         private final long mStartTimeDebug;
    242         private final VibrationEffect mEffect;
    243         private final VibrationEffect mOriginalEffect;
    244         private final int mUsageHint;
    245         private final int mUid;
    246         private final String mOpPkg;
    247 
    248         public VibrationInfo(long startTimeDebug, VibrationEffect effect,
    249                 VibrationEffect originalEffect, int usageHint, int uid, String opPkg) {
    250             mStartTimeDebug = startTimeDebug;
    251             mEffect = effect;
    252             mOriginalEffect = originalEffect;
    253             mUsageHint = usageHint;
    254             mUid = uid;
    255             mOpPkg = opPkg;
    256         }
    257 
    258         @Override
    259         public String toString() {
    260             return new StringBuilder()
    261                     .append("startTime: ")
    262                     .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug)))
    263                     .append(", effect: ")
    264                     .append(mEffect)
    265                     .append(", originalEffect: ")
    266                     .append(mOriginalEffect)
    267                     .append(", usageHint: ")
    268                     .append(mUsageHint)
    269                     .append(", uid: ")
    270                     .append(mUid)
    271                     .append(", opPkg: ")
    272                     .append(mOpPkg)
    273                     .toString();
    274         }
    275     }
    276 
    277     private static final class ScaleLevel {
    278         public final float gamma;
    279         public final int maxAmplitude;
    280 
    281         public ScaleLevel(float gamma) {
    282             this(gamma, VibrationEffect.MAX_AMPLITUDE);
    283         }
    284 
    285         public ScaleLevel(float gamma, int maxAmplitude) {
    286             this.gamma = gamma;
    287             this.maxAmplitude = maxAmplitude;
    288         }
    289 
    290         @Override
    291         public String toString() {
    292             return "ScaleLevel{gamma=" + gamma + ", maxAmplitude=" + maxAmplitude + "}";
    293         }
    294     }
    295 
    296     VibratorService(Context context) {
    297         vibratorInit();
    298         // Reset the hardware to a default state, in case this is a runtime
    299         // restart instead of a fresh boot.
    300         vibratorOff();
    301 
    302         mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
    303 
    304         mContext = context;
    305         PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
    306         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
    307         mWakeLock.setReferenceCounted(true);
    308 
    309         mAppOps = mContext.getSystemService(AppOpsManager.class);
    310         mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
    311                 BatteryStats.SERVICE_NAME));
    312 
    313         mPreviousVibrationsLimit = mContext.getResources().getInteger(
    314                 com.android.internal.R.integer.config_previousVibrationsDumpLimit);
    315 
    316         mDefaultVibrationAmplitude = mContext.getResources().getInteger(
    317                 com.android.internal.R.integer.config_defaultVibrationAmplitude);
    318 
    319         mAllowPriorityVibrationsInLowPowerMode = mContext.getResources().getBoolean(
    320                 com.android.internal.R.bool.config_allowPriorityVibrationsInLowPowerMode);
    321 
    322         mPreviousVibrations = new LinkedList<>();
    323 
    324         IntentFilter filter = new IntentFilter();
    325         filter.addAction(Intent.ACTION_SCREEN_OFF);
    326         context.registerReceiver(mIntentReceiver, filter);
    327 
    328         VibrationEffect clickEffect = createEffectFromResource(
    329                 com.android.internal.R.array.config_virtualKeyVibePattern);
    330         VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
    331                 DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/);
    332         VibrationEffect heavyClickEffect = createEffectFromResource(
    333                 com.android.internal.R.array.config_longPressVibePattern);
    334         VibrationEffect tickEffect = createEffectFromResource(
    335                 com.android.internal.R.array.config_clockTickVibePattern);
    336 
    337         mFallbackEffects = new SparseArray<>();
    338         mFallbackEffects.put(VibrationEffect.EFFECT_CLICK, clickEffect);
    339         mFallbackEffects.put(VibrationEffect.EFFECT_DOUBLE_CLICK, doubleClickEffect);
    340         mFallbackEffects.put(VibrationEffect.EFFECT_TICK, tickEffect);
    341         mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, heavyClickEffect);
    342 
    343         mScaleLevels = new SparseArray<>();
    344         mScaleLevels.put(SCALE_VERY_LOW,
    345                 new ScaleLevel(SCALE_VERY_LOW_GAMMA, SCALE_VERY_LOW_MAX_AMPLITUDE));
    346         mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_LOW_GAMMA, SCALE_LOW_MAX_AMPLITUDE));
    347         mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_NONE_GAMMA));
    348         mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_HIGH_GAMMA));
    349         mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_VERY_HIGH_GAMMA));
    350     }
    351 
    352     private VibrationEffect createEffectFromResource(int resId) {
    353         long[] timings = getLongIntArray(mContext.getResources(), resId);
    354         return createEffectFromTimings(timings);
    355     }
    356 
    357     private static VibrationEffect createEffectFromTimings(long[] timings) {
    358         if (timings == null || timings.length == 0) {
    359             return null;
    360         } else if (timings.length == 1) {
    361             return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE);
    362         } else {
    363             return VibrationEffect.createWaveform(timings, -1);
    364         }
    365     }
    366 
    367     public void systemReady() {
    368         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady");
    369         try {
    370             mIm = mContext.getSystemService(InputManager.class);
    371             mVibrator = mContext.getSystemService(Vibrator.class);
    372             mSettingObserver = new SettingsObserver(mH);
    373 
    374             mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
    375             mPowerManagerInternal.registerLowPowerModeObserver(
    376                     new PowerManagerInternal.LowPowerModeListener() {
    377                         @Override
    378                         public int getServiceType() {
    379                             return ServiceType.VIBRATION;
    380                         }
    381 
    382                         @Override
    383                         public void onLowPowerModeChanged(PowerSaveState result) {
    384                             updateVibrators();
    385                         }
    386             });
    387 
    388             mContext.getContentResolver().registerContentObserver(
    389                     Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
    390                     true, mSettingObserver, UserHandle.USER_ALL);
    391 
    392             mContext.getContentResolver().registerContentObserver(
    393                     Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY),
    394                     true, mSettingObserver, UserHandle.USER_ALL);
    395 
    396             mContext.getContentResolver().registerContentObserver(
    397                     Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY),
    398                     true, mSettingObserver, UserHandle.USER_ALL);
    399 
    400             mContext.registerReceiver(new BroadcastReceiver() {
    401                 @Override
    402                 public void onReceive(Context context, Intent intent) {
    403                     updateVibrators();
    404                 }
    405             }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
    406 
    407             updateVibrators();
    408         } finally {
    409             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
    410         }
    411     }
    412 
    413     private final class SettingsObserver extends ContentObserver {
    414         public SettingsObserver(Handler handler) {
    415             super(handler);
    416         }
    417 
    418         @Override
    419         public void onChange(boolean SelfChange) {
    420             updateVibrators();
    421         }
    422     }
    423 
    424     @Override // Binder call
    425     public boolean hasVibrator() {
    426         return doVibratorExists();
    427     }
    428 
    429     @Override // Binder call
    430     public boolean hasAmplitudeControl() {
    431         synchronized (mInputDeviceVibrators) {
    432             // Input device vibrators don't support amplitude controls yet, but are still used over
    433             // the system vibrator when connected.
    434             return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty();
    435         }
    436     }
    437 
    438     private void verifyIncomingUid(int uid) {
    439         if (uid == Binder.getCallingUid()) {
    440             return;
    441         }
    442         if (Binder.getCallingPid() == Process.myPid()) {
    443             return;
    444         }
    445         mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
    446                 Binder.getCallingPid(), Binder.getCallingUid(), null);
    447     }
    448 
    449     /**
    450      * Validate the incoming VibrationEffect.
    451      *
    452      * We can't throw exceptions here since we might be called from some system_server component,
    453      * which would bring the whole system down.
    454      *
    455      * @return whether the VibrationEffect is valid
    456      */
    457     private static boolean verifyVibrationEffect(VibrationEffect effect) {
    458         if (effect == null) {
    459             // Effect must not be null.
    460             Slog.wtf(TAG, "effect must not be null");
    461             return false;
    462         }
    463         try {
    464             effect.validate();
    465         } catch (Exception e) {
    466             Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e);
    467             return false;
    468         }
    469         return true;
    470     }
    471 
    472     private static long[] getLongIntArray(Resources r, int resid) {
    473         int[] ar = r.getIntArray(resid);
    474         if (ar == null) {
    475             return null;
    476         }
    477         long[] out = new long[ar.length];
    478         for (int i = 0; i < ar.length; i++) {
    479             out[i] = ar[i];
    480         }
    481         return out;
    482     }
    483 
    484     @Override // Binder call
    485     public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,
    486             IBinder token) {
    487         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate");
    488         try {
    489             if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
    490                     != PackageManager.PERMISSION_GRANTED) {
    491                 throw new SecurityException("Requires VIBRATE permission");
    492             }
    493             if (token == null) {
    494                 Slog.e(TAG, "token must not be null");
    495                 return;
    496             }
    497             verifyIncomingUid(uid);
    498             if (!verifyVibrationEffect(effect)) {
    499                 return;
    500             }
    501 
    502             // If our current vibration is longer than the new vibration and is the same amplitude,
    503             // then just let the current one finish.
    504             synchronized (mLock) {
    505                 if (effect instanceof VibrationEffect.OneShot
    506                         && mCurrentVibration != null
    507                         && mCurrentVibration.effect instanceof VibrationEffect.OneShot) {
    508                     VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
    509                     VibrationEffect.OneShot currentOneShot =
    510                             (VibrationEffect.OneShot) mCurrentVibration.effect;
    511                     if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration())
    512                             && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
    513                         if (DEBUG) {
    514                             Slog.d(TAG,
    515                                     "Ignoring incoming vibration in favor of current vibration");
    516                         }
    517                         return;
    518                     }
    519                 }
    520 
    521                 // If the current vibration is repeating and the incoming one is non-repeating,
    522                 // then ignore the non-repeating vibration. This is so that we don't cancel
    523                 // vibrations that are meant to grab the attention of the user, like ringtones and
    524                 // alarms, in favor of one-shot vibrations that are likely quite short.
    525                 if (!isRepeatingVibration(effect)
    526                         && mCurrentVibration != null
    527                         && isRepeatingVibration(mCurrentVibration.effect)) {
    528                     if (DEBUG) {
    529                         Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration");
    530                     }
    531                     return;
    532                 }
    533 
    534                 Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
    535                 linkVibration(vib);
    536                 long ident = Binder.clearCallingIdentity();
    537                 try {
    538                     doCancelVibrateLocked();
    539                     startVibrationLocked(vib);
    540                     addToPreviousVibrationsLocked(vib);
    541                 } finally {
    542                     Binder.restoreCallingIdentity(ident);
    543                 }
    544             }
    545         } finally {
    546             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
    547         }
    548     }
    549 
    550     private static boolean isRepeatingVibration(VibrationEffect effect) {
    551         return effect.getDuration() == Long.MAX_VALUE;
    552     }
    553 
    554     private void addToPreviousVibrationsLocked(Vibration vib) {
    555         if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
    556             mPreviousVibrations.removeFirst();
    557         }
    558         mPreviousVibrations.addLast(vib.toInfo());
    559     }
    560 
    561     @Override // Binder call
    562     public void cancelVibrate(IBinder token) {
    563         mContext.enforceCallingOrSelfPermission(
    564                 android.Manifest.permission.VIBRATE,
    565                 "cancelVibrate");
    566 
    567         synchronized (mLock) {
    568             if (mCurrentVibration != null && mCurrentVibration.token == token) {
    569                 if (DEBUG) {
    570                     Slog.d(TAG, "Canceling vibration.");
    571                 }
    572                 long ident = Binder.clearCallingIdentity();
    573                 try {
    574                     doCancelVibrateLocked();
    575                 } finally {
    576                     Binder.restoreCallingIdentity(ident);
    577                 }
    578             }
    579         }
    580     }
    581 
    582     private final Runnable mVibrationEndRunnable = new Runnable() {
    583         @Override
    584         public void run() {
    585             onVibrationFinished();
    586         }
    587     };
    588 
    589     @GuardedBy("mLock")
    590     private void doCancelVibrateLocked() {
    591         Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
    592         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked");
    593         try {
    594             mH.removeCallbacks(mVibrationEndRunnable);
    595             if (mThread != null) {
    596                 mThread.cancel();
    597                 mThread = null;
    598             }
    599             doVibratorOff();
    600             reportFinishVibrationLocked();
    601         } finally {
    602             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
    603         }
    604     }
    605 
    606     // Callback for whenever the current vibration has finished played out
    607     public void onVibrationFinished() {
    608         if (DEBUG) {
    609             Slog.e(TAG, "Vibration finished, cleaning up");
    610         }
    611         synchronized (mLock) {
    612             // Make sure the vibration is really done. This also reports that the vibration is
    613             // finished.
    614             doCancelVibrateLocked();
    615         }
    616     }
    617 
    618     @GuardedBy("mLock")
    619     private void startVibrationLocked(final Vibration vib) {
    620         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked");
    621         try {
    622             if (!isAllowedToVibrateLocked(vib)) {
    623                 return;
    624             }
    625 
    626             final int intensity = getCurrentIntensityLocked(vib);
    627             if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) {
    628                 return;
    629             }
    630 
    631             if (vib.isRingtone() && !shouldVibrateForRingtone()) {
    632                 if (DEBUG) {
    633                     Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
    634                 }
    635                 return;
    636             }
    637 
    638             final int mode = getAppOpMode(vib);
    639             if (mode != AppOpsManager.MODE_ALLOWED) {
    640                 if (mode == AppOpsManager.MODE_ERRORED) {
    641                     // We might be getting calls from within system_server, so we don't actually
    642                     // want to throw a SecurityException here.
    643                     Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid);
    644                 }
    645                 return;
    646             }
    647             applyVibrationIntensityScalingLocked(vib, intensity);
    648             startVibrationInnerLocked(vib);
    649         } finally {
    650             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
    651         }
    652     }
    653 
    654     @GuardedBy("mLock")
    655     private void startVibrationInnerLocked(Vibration vib) {
    656         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked");
    657         try {
    658             mCurrentVibration = vib;
    659             if (vib.effect instanceof VibrationEffect.OneShot) {
    660                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
    661                 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
    662                 doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint);
    663                 mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration());
    664             } else if (vib.effect instanceof VibrationEffect.Waveform) {
    665                 // mThread better be null here. doCancelVibrate should always be
    666                 // called before startNextVibrationLocked or startVibrationLocked.
    667                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
    668                 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
    669                 mThread = new VibrateThread(waveform, vib.uid, vib.usageHint);
    670                 mThread.start();
    671             } else if (vib.effect instanceof VibrationEffect.Prebaked) {
    672                 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0);
    673                 long timeout = doVibratorPrebakedEffectLocked(vib);
    674                 if (timeout > 0) {
    675                     mH.postDelayed(mVibrationEndRunnable, timeout);
    676                 }
    677             } else {
    678                 Slog.e(TAG, "Unknown vibration type, ignoring");
    679             }
    680         } finally {
    681             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
    682         }
    683     }
    684 
    685     private boolean isAllowedToVibrateLocked(Vibration vib) {
    686         if (!mLowPowerMode) {
    687             return true;
    688         }
    689 
    690         if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
    691             return true;
    692         }
    693 
    694         if (vib.usageHint == AudioAttributes.USAGE_ALARM ||
    695                 vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY ||
    696                 vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) {
    697             return true;
    698         }
    699 
    700         return false;
    701     }
    702 
    703     private int getCurrentIntensityLocked(Vibration vib) {
    704         if (vib.isNotification() || vib.isRingtone()){
    705             return mNotificationIntensity;
    706         } else if (vib.isHapticFeedback()) {
    707             return mHapticFeedbackIntensity;
    708         } else {
    709             return Vibrator.VIBRATION_INTENSITY_MEDIUM;
    710         }
    711     }
    712 
    713     /**
    714      * Scale the vibration effect by the intensity as appropriate based its intent.
    715      */
    716     private void applyVibrationIntensityScalingLocked(Vibration vib, int intensity) {
    717         if (vib.effect instanceof VibrationEffect.Prebaked) {
    718             // Prebaked effects are always just a direct translation from intensity to
    719             // EffectStrength.
    720             VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked)vib.effect;
    721             prebaked.setEffectStrength(intensityToEffectStrength(intensity));
    722             return;
    723         }
    724 
    725         final int defaultIntensity;
    726         if (vib.isNotification() || vib.isRingtone()) {
    727             defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity();
    728         } else if (vib.isHapticFeedback()) {
    729             defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity();
    730         } else {
    731             // If we don't know what kind of vibration we're playing then just skip scaling for
    732             // now.
    733             return;
    734         }
    735 
    736         final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity);
    737         if (scale == null) {
    738             // We should have scaling levels for all cases, so not being able to scale because of a
    739             // missing level is unexpected.
    740             Slog.e(TAG, "No configured scaling level!"
    741                     + " (current=" + intensity + ", default= " + defaultIntensity + ")");
    742             return;
    743         }
    744 
    745         VibrationEffect scaledEffect = null;
    746         if (vib.effect instanceof VibrationEffect.OneShot) {
    747             VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect;
    748             oneShot = oneShot.resolve(mDefaultVibrationAmplitude);
    749             scaledEffect = oneShot.scale(scale.gamma, scale.maxAmplitude);
    750         } else if (vib.effect instanceof VibrationEffect.Waveform) {
    751             VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect;
    752             waveform = waveform.resolve(mDefaultVibrationAmplitude);
    753             scaledEffect = waveform.scale(scale.gamma, scale.maxAmplitude);
    754         } else {
    755             Slog.w(TAG, "Unable to apply intensity scaling, unknown VibrationEffect type");
    756         }
    757 
    758         if (scaledEffect != null) {
    759             vib.originalEffect = vib.effect;
    760             vib.effect = scaledEffect;
    761         }
    762     }
    763 
    764     private boolean shouldVibrateForRingtone() {
    765         AudioManager audioManager = mContext.getSystemService(AudioManager.class);
    766         int ringerMode = audioManager.getRingerModeInternal();
    767         // "Also vibrate for calls" Setting in Sound
    768         if (Settings.System.getInt(
    769                 mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) {
    770             return ringerMode != AudioManager.RINGER_MODE_SILENT;
    771         } else {
    772             return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
    773         }
    774     }
    775 
    776     private int getAppOpMode(Vibration vib) {
    777         int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE,
    778                 vib.usageHint, vib.uid, vib.opPkg);
    779         if (mode == AppOpsManager.MODE_ALLOWED) {
    780             mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg);
    781         }
    782         return mode;
    783     }
    784 
    785     @GuardedBy("mLock")
    786     private void reportFinishVibrationLocked() {
    787         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked");
    788         try {
    789             if (mCurrentVibration != null) {
    790                 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid,
    791                         mCurrentVibration.opPkg);
    792                 unlinkVibration(mCurrentVibration);
    793                 mCurrentVibration = null;
    794             }
    795         } finally {
    796             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
    797         }
    798     }
    799 
    800     private void linkVibration(Vibration vib) {
    801         // Only link against waveforms since they potentially don't have a finish if
    802         // they're repeating. Let other effects just play out until they're done.
    803         if (vib.effect instanceof VibrationEffect.Waveform) {
    804             try {
    805                 vib.token.linkToDeath(vib, 0);
    806             } catch (RemoteException e) {
    807                 return;
    808             }
    809         }
    810     }
    811 
    812     private void unlinkVibration(Vibration vib) {
    813         if (vib.effect instanceof VibrationEffect.Waveform) {
    814             vib.token.unlinkToDeath(vib, 0);
    815         }
    816     }
    817 
    818     private void updateVibrators() {
    819         synchronized (mLock) {
    820             boolean devicesUpdated = updateInputDeviceVibratorsLocked();
    821             boolean lowPowerModeUpdated = updateLowPowerModeLocked();
    822             updateVibrationIntensityLocked();
    823 
    824             if (devicesUpdated || lowPowerModeUpdated) {
    825                 // If the state changes out from under us then just reset.
    826                 doCancelVibrateLocked();
    827             }
    828         }
    829     }
    830 
    831     private boolean updateInputDeviceVibratorsLocked() {
    832         boolean changed = false;
    833         boolean vibrateInputDevices = false;
    834         try {
    835             vibrateInputDevices = Settings.System.getIntForUser(
    836                     mContext.getContentResolver(),
    837                     Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
    838         } catch (SettingNotFoundException snfe) {
    839         }
    840         if (vibrateInputDevices != mVibrateInputDevicesSetting) {
    841             changed = true;
    842             mVibrateInputDevicesSetting = vibrateInputDevices;
    843         }
    844 
    845         if (mVibrateInputDevicesSetting) {
    846             if (!mInputDeviceListenerRegistered) {
    847                 mInputDeviceListenerRegistered = true;
    848                 mIm.registerInputDeviceListener(this, mH);
    849             }
    850         } else {
    851             if (mInputDeviceListenerRegistered) {
    852                 mInputDeviceListenerRegistered = false;
    853                 mIm.unregisterInputDeviceListener(this);
    854             }
    855         }
    856 
    857         mInputDeviceVibrators.clear();
    858         if (mVibrateInputDevicesSetting) {
    859             int[] ids = mIm.getInputDeviceIds();
    860             for (int i = 0; i < ids.length; i++) {
    861                 InputDevice device = mIm.getInputDevice(ids[i]);
    862                 Vibrator vibrator = device.getVibrator();
    863                 if (vibrator.hasVibrator()) {
    864                     mInputDeviceVibrators.add(vibrator);
    865                 }
    866             }
    867             return true;
    868         }
    869         return changed;
    870     }
    871 
    872     private boolean updateLowPowerModeLocked() {
    873         boolean lowPowerMode = mPowerManagerInternal
    874                 .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
    875         if (lowPowerMode != mLowPowerMode) {
    876             mLowPowerMode = lowPowerMode;
    877             return true;
    878         }
    879         return false;
    880     }
    881 
    882     private void updateVibrationIntensityLocked() {
    883         mHapticFeedbackIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
    884                 Settings.System.HAPTIC_FEEDBACK_INTENSITY,
    885                 mVibrator.getDefaultHapticFeedbackIntensity(), UserHandle.USER_CURRENT);
    886         mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(),
    887                 Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
    888                 mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT);
    889     }
    890 
    891     @Override
    892     public void onInputDeviceAdded(int deviceId) {
    893         updateVibrators();
    894     }
    895 
    896     @Override
    897     public void onInputDeviceChanged(int deviceId) {
    898         updateVibrators();
    899     }
    900 
    901     @Override
    902     public void onInputDeviceRemoved(int deviceId) {
    903         updateVibrators();
    904     }
    905 
    906     private boolean doVibratorExists() {
    907         // For now, we choose to ignore the presence of input devices that have vibrators
    908         // when reporting whether the device has a vibrator.  Applications often use this
    909         // information to decide whether to enable certain features so they expect the
    910         // result of hasVibrator() to be constant.  For now, just report whether
    911         // the device has a built-in vibrator.
    912         //synchronized (mInputDeviceVibrators) {
    913         //    return !mInputDeviceVibrators.isEmpty() || vibratorExists();
    914         //}
    915         return vibratorExists();
    916     }
    917 
    918     private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
    919         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn");
    920         try {
    921             synchronized (mInputDeviceVibrators) {
    922                 if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
    923                     amplitude = mDefaultVibrationAmplitude;
    924                 }
    925                 if (DEBUG) {
    926                     Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
    927                             " with amplitude " + amplitude + ".");
    928                 }
    929                 noteVibratorOnLocked(uid, millis);
    930                 final int vibratorCount = mInputDeviceVibrators.size();
    931                 if (vibratorCount != 0) {
    932                     final AudioAttributes attributes =
    933                             new AudioAttributes.Builder().setUsage(usageHint).build();
    934                     for (int i = 0; i < vibratorCount; i++) {
    935                         mInputDeviceVibrators.get(i).vibrate(millis, attributes);
    936                     }
    937                 } else {
    938                     // Note: ordering is important here! Many haptic drivers will reset their
    939                     // amplitude when enabled, so we always have to enable frst, then set the
    940                     // amplitude.
    941                     vibratorOn(millis);
    942                     doVibratorSetAmplitude(amplitude);
    943                 }
    944             }
    945         } finally {
    946             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
    947         }
    948     }
    949 
    950     private void doVibratorSetAmplitude(int amplitude) {
    951         if (mSupportsAmplitudeControl) {
    952             vibratorSetAmplitude(amplitude);
    953         }
    954     }
    955 
    956     private void doVibratorOff() {
    957         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff");
    958         try {
    959             synchronized (mInputDeviceVibrators) {
    960                 if (DEBUG) {
    961                     Slog.d(TAG, "Turning vibrator off.");
    962                 }
    963                 noteVibratorOffLocked();
    964                 final int vibratorCount = mInputDeviceVibrators.size();
    965                 if (vibratorCount != 0) {
    966                     for (int i = 0; i < vibratorCount; i++) {
    967                         mInputDeviceVibrators.get(i).cancel();
    968                     }
    969                 } else {
    970                     vibratorOff();
    971                 }
    972             }
    973         } finally {
    974             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
    975         }
    976     }
    977 
    978     @GuardedBy("mLock")
    979     private long doVibratorPrebakedEffectLocked(Vibration vib) {
    980         Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked");
    981         try {
    982             final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect;
    983             final boolean usingInputDeviceVibrators;
    984             synchronized (mInputDeviceVibrators) {
    985                 usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty();
    986             }
    987             // Input devices don't support prebaked effect, so skip trying it with them.
    988             if (!usingInputDeviceVibrators) {
    989                 long timeout = vibratorPerformEffect(prebaked.getId(),
    990                         prebaked.getEffectStrength());
    991                 if (timeout > 0) {
    992                     noteVibratorOnLocked(vib.uid, timeout);
    993                     return timeout;
    994                 }
    995             }
    996             if (!prebaked.shouldFallback()) {
    997                 return 0;
    998             }
    999             VibrationEffect effect = getFallbackEffect(prebaked.getId());
   1000             if (effect == null) {
   1001                 Slog.w(TAG, "Failed to play prebaked effect, no fallback");
   1002                 return 0;
   1003             }
   1004             Vibration fallbackVib =
   1005                     new Vibration(vib.token, effect, vib.usageHint, vib.uid, vib.opPkg);
   1006             final int intensity = getCurrentIntensityLocked(fallbackVib);
   1007             linkVibration(fallbackVib);
   1008             applyVibrationIntensityScalingLocked(fallbackVib, intensity);
   1009             startVibrationInnerLocked(fallbackVib);
   1010             return 0;
   1011         } finally {
   1012             Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
   1013         }
   1014     }
   1015 
   1016     private VibrationEffect getFallbackEffect(int effectId) {
   1017         return mFallbackEffects.get(effectId);
   1018     }
   1019 
   1020     /**
   1021      * Return the current desired effect strength.
   1022      *
   1023      * If the returned value is &lt; 0 then the vibration shouldn't be played at all.
   1024      */
   1025     private static int intensityToEffectStrength(int intensity) {
   1026         switch (intensity) {
   1027             case Vibrator.VIBRATION_INTENSITY_LOW:
   1028                 return EffectStrength.LIGHT;
   1029             case Vibrator.VIBRATION_INTENSITY_MEDIUM:
   1030                 return EffectStrength.MEDIUM;
   1031             case Vibrator.VIBRATION_INTENSITY_HIGH:
   1032                 return EffectStrength.STRONG;
   1033             default:
   1034                 Slog.w(TAG, "Got unexpected vibration intensity: " + intensity);
   1035                 return EffectStrength.STRONG;
   1036         }
   1037     }
   1038 
   1039     private void noteVibratorOnLocked(int uid, long millis) {
   1040         try {
   1041             mBatteryStatsService.noteVibratorOn(uid, millis);
   1042             mCurVibUid = uid;
   1043         } catch (RemoteException e) {
   1044         }
   1045     }
   1046 
   1047     private void noteVibratorOffLocked() {
   1048         if (mCurVibUid >= 0) {
   1049             try {
   1050                 mBatteryStatsService.noteVibratorOff(mCurVibUid);
   1051             } catch (RemoteException e) { }
   1052             mCurVibUid = -1;
   1053         }
   1054     }
   1055 
   1056     private class VibrateThread extends Thread {
   1057         private final VibrationEffect.Waveform mWaveform;
   1058         private final int mUid;
   1059         private final int mUsageHint;
   1060 
   1061         private boolean mForceStop;
   1062 
   1063         VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
   1064             mWaveform = waveform;
   1065             mUid = uid;
   1066             mUsageHint = usageHint;
   1067             mTmpWorkSource.set(uid);
   1068             mWakeLock.setWorkSource(mTmpWorkSource);
   1069         }
   1070 
   1071         private long delayLocked(long duration) {
   1072             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "delayLocked");
   1073             try {
   1074                 long durationRemaining = duration;
   1075                 if (duration > 0) {
   1076                     final long bedtime = duration + SystemClock.uptimeMillis();
   1077                     do {
   1078                         try {
   1079                             this.wait(durationRemaining);
   1080                         }
   1081                         catch (InterruptedException e) { }
   1082                         if (mForceStop) {
   1083                             break;
   1084                         }
   1085                         durationRemaining = bedtime - SystemClock.uptimeMillis();
   1086                     } while (durationRemaining > 0);
   1087                     return duration - durationRemaining;
   1088                 }
   1089                 return 0;
   1090             } finally {
   1091                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
   1092             }
   1093         }
   1094 
   1095         public void run() {
   1096             Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
   1097             mWakeLock.acquire();
   1098             try {
   1099                 boolean finished = playWaveform();
   1100                 if (finished) {
   1101                     onVibrationFinished();
   1102                 }
   1103             } finally {
   1104                 mWakeLock.release();
   1105             }
   1106         }
   1107 
   1108         /**
   1109          * Play the waveform.
   1110          *
   1111          * @return true if it finished naturally, false otherwise (e.g. it was canceled).
   1112          */
   1113         public boolean playWaveform() {
   1114             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playWaveform");
   1115             try {
   1116                 synchronized (this) {
   1117                     final long[] timings = mWaveform.getTimings();
   1118                     final int[] amplitudes = mWaveform.getAmplitudes();
   1119                     final int len = timings.length;
   1120                     final int repeat = mWaveform.getRepeatIndex();
   1121 
   1122                     int index = 0;
   1123                     long onDuration = 0;
   1124                     while (!mForceStop) {
   1125                         if (index < len) {
   1126                             final int amplitude = amplitudes[index];
   1127                             final long duration = timings[index++];
   1128                             if (duration <= 0) {
   1129                                 continue;
   1130                             }
   1131                             if (amplitude != 0) {
   1132                                 if (onDuration <= 0) {
   1133                                     // Telling the vibrator to start multiple times usually causes
   1134                                     // effects to feel "choppy" because the motor resets at every on
   1135                                     // command.  Instead we figure out how long our next "on" period
   1136                                     // is going to be, tell the motor to stay on for the full
   1137                                     // duration, and then wake up to change the amplitude at the
   1138                                     // appropriate intervals.
   1139                                     onDuration = getTotalOnDuration(timings, amplitudes, index - 1,
   1140                                             repeat);
   1141                                     doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
   1142                                 } else {
   1143                                     doVibratorSetAmplitude(amplitude);
   1144                                 }
   1145                             }
   1146 
   1147                             long waitTime = delayLocked(duration);
   1148                             if (amplitude != 0) {
   1149                                 onDuration -= waitTime;
   1150                             }
   1151                         } else if (repeat < 0) {
   1152                             break;
   1153                         } else {
   1154                             index = repeat;
   1155                         }
   1156                     }
   1157                     return !mForceStop;
   1158                 }
   1159             } finally {
   1160                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
   1161             }
   1162         }
   1163 
   1164         public void cancel() {
   1165             synchronized (this) {
   1166                 mThread.mForceStop = true;
   1167                 mThread.notify();
   1168             }
   1169         }
   1170 
   1171         /**
   1172          * Get the duration the vibrator will be on starting at startIndex until the next time it's
   1173          * off.
   1174          */
   1175         private long getTotalOnDuration(
   1176                 long[] timings, int[] amplitudes, int startIndex, int repeatIndex) {
   1177             int i = startIndex;
   1178             long timing = 0;
   1179             while(amplitudes[i] != 0) {
   1180                 timing += timings[i++];
   1181                 if (i >= timings.length) {
   1182                     if (repeatIndex >= 0) {
   1183                         i = repeatIndex;
   1184                     } else {
   1185                         break;
   1186                     }
   1187                 }
   1188                 if (i == startIndex) {
   1189                     return 1000;
   1190                 }
   1191             }
   1192             return timing;
   1193         }
   1194     }
   1195 
   1196     BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
   1197         @Override
   1198         public void onReceive(Context context, Intent intent) {
   1199             if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
   1200                 synchronized (mLock) {
   1201                     // When the system is entering a non-interactive state, we want
   1202                     // to cancel vibrations in case a misbehaving app has abandoned
   1203                     // them.  However it may happen that the system is currently playing
   1204                     // haptic feedback as part of the transition.  So we don't cancel
   1205                     // system vibrations.
   1206                     if (mCurrentVibration != null
   1207                             && !(mCurrentVibration.isHapticFeedback()
   1208                                 && mCurrentVibration.isFromSystem())) {
   1209                         doCancelVibrateLocked();
   1210                     }
   1211                 }
   1212             }
   1213         }
   1214     };
   1215 
   1216     @Override
   1217     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
   1218         if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
   1219 
   1220         pw.println("Vibrator Service:");
   1221         synchronized (mLock) {
   1222             pw.print("  mCurrentVibration=");
   1223             if (mCurrentVibration != null) {
   1224                 pw.println(mCurrentVibration.toInfo().toString());
   1225             } else {
   1226                 pw.println("null");
   1227             }
   1228             pw.println("  mLowPowerMode=" + mLowPowerMode);
   1229             pw.println("  mHapticFeedbackIntensity=" + mHapticFeedbackIntensity);
   1230             pw.println("  mNotificationIntensity=" + mNotificationIntensity);
   1231             pw.println("");
   1232             pw.println("  Previous vibrations:");
   1233             for (VibrationInfo info : mPreviousVibrations) {
   1234                 pw.print("    ");
   1235                 pw.println(info.toString());
   1236             }
   1237         }
   1238     }
   1239 
   1240     @Override
   1241     public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
   1242             String[] args, ShellCallback callback, ResultReceiver resultReceiver)
   1243             throws RemoteException {
   1244         new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
   1245     }
   1246 
   1247     private final class VibratorShellCommand extends ShellCommand {
   1248 
   1249         private static final long MAX_VIBRATION_MS = 200;
   1250 
   1251         private final IBinder mToken;
   1252 
   1253         private VibratorShellCommand(IBinder token) {
   1254             mToken = token;
   1255         }
   1256 
   1257         @Override
   1258         public int onCommand(String cmd) {
   1259             if ("vibrate".equals(cmd)) {
   1260                 return runVibrate();
   1261             }
   1262             return handleDefaultCommands(cmd);
   1263         }
   1264 
   1265         private int runVibrate() {
   1266             Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate");
   1267             try {
   1268                 try {
   1269                     final int zenMode = Settings.Global.getInt(mContext.getContentResolver(),
   1270                             Settings.Global.ZEN_MODE);
   1271                     if (zenMode != Settings.Global.ZEN_MODE_OFF) {
   1272                         try (PrintWriter pw = getOutPrintWriter();) {
   1273                             pw.print("Ignoring because device is on DND mode ");
   1274                             pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_",
   1275                                     zenMode));
   1276                             return 0;
   1277                         }
   1278                     }
   1279                 } catch (SettingNotFoundException e) {
   1280                     // ignore
   1281                 }
   1282 
   1283                 final long duration = Long.parseLong(getNextArgRequired());
   1284                 if (duration > MAX_VIBRATION_MS) {
   1285                     throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS);
   1286                 }
   1287                 String description = getNextArg();
   1288                 if (description == null) {
   1289                     description = "Shell command";
   1290                 }
   1291 
   1292                 VibrationEffect effect =
   1293                         VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
   1294                 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
   1295                         mToken);
   1296                 return 0;
   1297             } finally {
   1298                 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR);
   1299             }
   1300         }
   1301 
   1302         @Override
   1303         public void onHelp() {
   1304             try (PrintWriter pw = getOutPrintWriter();) {
   1305                 pw.println("Vibrator commands:");
   1306                 pw.println("  help");
   1307                 pw.println("    Prints this help text.");
   1308                 pw.println("");
   1309                 pw.println("  vibrate duration [description]");
   1310                 pw.println("    Vibrates for duration milliseconds; ignored when device is on DND ");
   1311                 pw.println("    (Do Not Disturb) mode.");
   1312                 pw.println("");
   1313             }
   1314         }
   1315     }
   1316 
   1317 }
   1318