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