• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.server;
18 
19 import android.app.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 
vibratorExists()142     native static boolean vibratorExists();
vibratorInit()143     native static void vibratorInit();
vibratorOn(long milliseconds)144     native static void vibratorOn(long milliseconds);
vibratorOff()145     native static void vibratorOff();
vibratorSupportsAmplitudeControl()146     native static boolean vibratorSupportsAmplitudeControl();
vibratorSetAmplitude(int amplitude)147     native static void vibratorSetAmplitude(int amplitude);
vibratorPerformEffect(long effect, long strength)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 
Vibration(IBinder token, VibrationEffect effect, int usageHint, int uid, String opPkg)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 
binderDied()180         public void binderDied() {
181             synchronized (mLock) {
182                 if (this == mCurrentVibration) {
183                     doCancelVibrateLocked();
184                 }
185             }
186         }
187 
hasTimeoutLongerThan(long millis)188         public boolean hasTimeoutLongerThan(long millis) {
189             final long duration = effect.getDuration();
190             return duration >= 0 && duration > millis;
191         }
192 
isHapticFeedback()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 
isNotification()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 
isRingtone()226         public boolean isRingtone() {
227             return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
228         }
229 
isFromSystem()230         public boolean isFromSystem() {
231             return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg);
232         }
233 
toInfo()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 
VibrationInfo(long startTimeDebug, VibrationEffect effect, VibrationEffect originalEffect, int usageHint, int uid, String opPkg)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
toString()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 
ScaleLevel(float gamma)281         public ScaleLevel(float gamma) {
282             this(gamma, VibrationEffect.MAX_AMPLITUDE);
283         }
284 
ScaleLevel(float gamma, int maxAmplitude)285         public ScaleLevel(float gamma, int maxAmplitude) {
286             this.gamma = gamma;
287             this.maxAmplitude = maxAmplitude;
288         }
289 
290         @Override
toString()291         public String toString() {
292             return "ScaleLevel{gamma=" + gamma + ", maxAmplitude=" + maxAmplitude + "}";
293         }
294     }
295 
VibratorService(Context context)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 
createEffectFromResource(int resId)352     private VibrationEffect createEffectFromResource(int resId) {
353         long[] timings = getLongIntArray(mContext.getResources(), resId);
354         return createEffectFromTimings(timings);
355     }
356 
createEffectFromTimings(long[] timings)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 
systemReady()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 {
SettingsObserver(Handler handler)414         public SettingsObserver(Handler handler) {
415             super(handler);
416         }
417 
418         @Override
onChange(boolean SelfChange)419         public void onChange(boolean SelfChange) {
420             updateVibrators();
421         }
422     }
423 
424     @Override // Binder call
hasVibrator()425     public boolean hasVibrator() {
426         return doVibratorExists();
427     }
428 
429     @Override // Binder call
hasAmplitudeControl()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 
verifyIncomingUid(int uid)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      */
verifyVibrationEffect(VibrationEffect effect)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 
getLongIntArray(Resources r, int resid)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
vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, IBinder token)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 
isRepeatingVibration(VibrationEffect effect)550     private static boolean isRepeatingVibration(VibrationEffect effect) {
551         return effect.getDuration() == Long.MAX_VALUE;
552     }
553 
addToPreviousVibrationsLocked(Vibration vib)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
cancelVibrate(IBinder token)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")
doCancelVibrateLocked()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
onVibrationFinished()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")
startVibrationLocked(final Vibration vib)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")
startVibrationInnerLocked(Vibration vib)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 
isAllowedToVibrateLocked(Vibration vib)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 
getCurrentIntensityLocked(Vibration vib)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      */
applyVibrationIntensityScalingLocked(Vibration vib, int intensity)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 
shouldVibrateForRingtone()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 
getAppOpMode(Vibration vib)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")
reportFinishVibrationLocked()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 
linkVibration(Vibration vib)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 
unlinkVibration(Vibration vib)812     private void unlinkVibration(Vibration vib) {
813         if (vib.effect instanceof VibrationEffect.Waveform) {
814             vib.token.unlinkToDeath(vib, 0);
815         }
816     }
817 
updateVibrators()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 
updateInputDeviceVibratorsLocked()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 
updateLowPowerModeLocked()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 
updateVibrationIntensityLocked()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
onInputDeviceAdded(int deviceId)892     public void onInputDeviceAdded(int deviceId) {
893         updateVibrators();
894     }
895 
896     @Override
onInputDeviceChanged(int deviceId)897     public void onInputDeviceChanged(int deviceId) {
898         updateVibrators();
899     }
900 
901     @Override
onInputDeviceRemoved(int deviceId)902     public void onInputDeviceRemoved(int deviceId) {
903         updateVibrators();
904     }
905 
doVibratorExists()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 
doVibratorOn(long millis, int amplitude, int uid, int usageHint)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 
doVibratorSetAmplitude(int amplitude)950     private void doVibratorSetAmplitude(int amplitude) {
951         if (mSupportsAmplitudeControl) {
952             vibratorSetAmplitude(amplitude);
953         }
954     }
955 
doVibratorOff()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")
doVibratorPrebakedEffectLocked(Vibration vib)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 
getFallbackEffect(int effectId)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      */
intensityToEffectStrength(int intensity)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 
noteVibratorOnLocked(int uid, long millis)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 
noteVibratorOffLocked()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 
VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint)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 
delayLocked(long duration)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 
run()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          */
playWaveform()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 
cancel()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          */
getTotalOnDuration( long[] timings, int[] amplitudes, int startIndex, int repeatIndex)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
dump(FileDescriptor fd, PrintWriter pw, String[] args)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
onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, String[] args, ShellCallback callback, ResultReceiver resultReceiver)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 
VibratorShellCommand(IBinder token)1253         private VibratorShellCommand(IBinder token) {
1254             mToken = token;
1255         }
1256 
1257         @Override
onCommand(String cmd)1258         public int onCommand(String cmd) {
1259             if ("vibrate".equals(cmd)) {
1260                 return runVibrate();
1261             }
1262             return handleDefaultCommands(cmd);
1263         }
1264 
runVibrate()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
onHelp()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