VibratorService.java revision 017939e43ceb847333d1115258c3db1411dbc03a
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.Constants.EffectStrength;
29import android.media.AudioManager;
30import android.os.PowerSaveState;
31import android.os.BatteryStats;
32import android.os.Handler;
33import android.os.IVibratorService;
34import android.os.PowerManager;
35import android.os.PowerManagerInternal;
36import android.os.Process;
37import android.os.RemoteException;
38import android.os.ResultReceiver;
39import android.os.IBinder;
40import android.os.Binder;
41import android.os.ServiceManager;
42import android.os.ShellCallback;
43import android.os.ShellCommand;
44import android.os.SystemClock;
45import android.os.UserHandle;
46import android.os.Vibrator;
47import android.os.VibrationEffect;
48import android.os.WorkSource;
49import android.provider.Settings;
50import android.provider.Settings.SettingNotFoundException;
51import android.util.Slog;
52import android.view.InputDevice;
53import android.media.AudioAttributes;
54
55import com.android.internal.app.IAppOpsService;
56import com.android.internal.app.IBatteryStats;
57import com.android.internal.util.DumpUtils;
58import com.android.server.power.BatterySaverPolicy.ServiceType;
59
60import java.io.FileDescriptor;
61import java.io.PrintWriter;
62import java.util.ArrayList;
63import java.util.Arrays;
64import java.util.Iterator;
65import java.util.LinkedList;
66import java.util.ListIterator;
67
68public class VibratorService extends IVibratorService.Stub
69        implements InputManager.InputDeviceListener {
70    private static final String TAG = "VibratorService";
71    private static final boolean DEBUG = false;
72    private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
73
74    private final LinkedList<VibrationInfo> mPreviousVibrations;
75    private final int mPreviousVibrationsLimit;
76    private final boolean mSupportsAmplitudeControl;
77    private final int mDefaultVibrationAmplitude;
78    private final VibrationEffect[] mFallbackEffects;
79    private final WorkSource mTmpWorkSource = new WorkSource();
80    private final Handler mH = new Handler();
81    private final Object mLock = new Object();
82
83    private final Context mContext;
84    private final PowerManager.WakeLock mWakeLock;
85    private final IAppOpsService mAppOpsService;
86    private final IBatteryStats mBatteryStatsService;
87    private PowerManagerInternal mPowerManagerInternal;
88    private InputManager mIm;
89
90    private volatile VibrateThread mThread;
91
92    // mInputDeviceVibrators lock should be acquired after mLock, if both are
93    // to be acquired
94    private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
95    private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
96    private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
97
98    private Vibration mCurrentVibration;
99    private int mCurVibUid = -1;
100    private boolean mLowPowerMode;
101    private SettingsObserver mSettingObserver;
102
103    native static boolean vibratorExists();
104    native static void vibratorInit();
105    native static void vibratorOn(long milliseconds);
106    native static void vibratorOff();
107    native static boolean vibratorSupportsAmplitudeControl();
108    native static void vibratorSetAmplitude(int amplitude);
109    native static long vibratorPerformEffect(long effect, long strength);
110
111    private class Vibration implements IBinder.DeathRecipient {
112        private final IBinder mToken;
113        private final VibrationEffect mEffect;
114        private final long mStartTime;
115        private final int mUsageHint;
116        private final int mUid;
117        private final String mOpPkg;
118
119        private Vibration(IBinder token, VibrationEffect effect,
120                int usageHint, int uid, String opPkg) {
121            mToken = token;
122            mEffect = effect;
123            mStartTime = SystemClock.uptimeMillis();
124            mUsageHint = usageHint;
125            mUid = uid;
126            mOpPkg = opPkg;
127        }
128
129        public void binderDied() {
130            synchronized (mLock) {
131                if (this == mCurrentVibration) {
132                    doCancelVibrateLocked();
133                }
134            }
135        }
136
137        public boolean hasLongerTimeout(long millis) {
138            // If the current effect is a one shot vibration that will end after the given timeout
139            // for the new one shot vibration, then just let the current vibration finish. All
140            // other effect types will get pre-empted.
141            if (mEffect instanceof VibrationEffect.OneShot) {
142                VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) mEffect;
143                return mStartTime + oneShot.getTiming() > SystemClock.uptimeMillis() + millis;
144            }
145            return false;
146        }
147
148        public boolean isSystemHapticFeedback() {
149            boolean repeating = false;
150            if (mEffect instanceof VibrationEffect.Waveform) {
151                VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) mEffect;
152                repeating = (waveform.getRepeatIndex() < 0);
153            }
154            return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg))
155                    && !repeating;
156        }
157    }
158
159    private static class VibrationInfo {
160        private final long mStartTime;
161        private final VibrationEffect mEffect;
162        private final int mUsageHint;
163        private final int mUid;
164        private final String mOpPkg;
165
166        public VibrationInfo(long startTime, VibrationEffect effect,
167                int usageHint, int uid, String opPkg) {
168            mStartTime = startTime;
169            mEffect = effect;
170            mUsageHint = usageHint;
171            mUid = uid;
172            mOpPkg = opPkg;
173        }
174
175        @Override
176        public String toString() {
177            return new StringBuilder()
178                    .append(", startTime: ")
179                    .append(mStartTime)
180                    .append(", effect: ")
181                    .append(mEffect)
182                    .append(", usageHint: ")
183                    .append(mUsageHint)
184                    .append(", uid: ")
185                    .append(mUid)
186                    .append(", opPkg: ")
187                    .append(mOpPkg)
188                    .toString();
189        }
190    }
191
192    VibratorService(Context context) {
193        vibratorInit();
194        // Reset the hardware to a default state, in case this is a runtime
195        // restart instead of a fresh boot.
196        vibratorOff();
197
198        mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl();
199
200        mContext = context;
201        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
202        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
203        mWakeLock.setReferenceCounted(true);
204
205        mAppOpsService =
206            IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
207        mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
208                BatteryStats.SERVICE_NAME));
209
210        mPreviousVibrationsLimit = mContext.getResources().getInteger(
211                com.android.internal.R.integer.config_previousVibrationsDumpLimit);
212
213        mDefaultVibrationAmplitude = mContext.getResources().getInteger(
214                com.android.internal.R.integer.config_defaultVibrationAmplitude);
215
216        mPreviousVibrations = new LinkedList<>();
217
218        IntentFilter filter = new IntentFilter();
219        filter.addAction(Intent.ACTION_SCREEN_OFF);
220        context.registerReceiver(mIntentReceiver, filter);
221
222        long[] clickEffectTimings = getLongIntArray(context.getResources(),
223                com.android.internal.R.array.config_virtualKeyVibePattern);
224        VibrationEffect clickEffect;
225        if (clickEffectTimings.length == 0) {
226            clickEffect = null;
227        } else if (clickEffectTimings.length == 1) {
228            clickEffect = VibrationEffect.createOneShot(
229                    clickEffectTimings[0], VibrationEffect.DEFAULT_AMPLITUDE);
230        } else {
231            clickEffect = VibrationEffect.createWaveform(clickEffectTimings, -1);
232        }
233        VibrationEffect doubleClickEffect = VibrationEffect.createWaveform(
234                new long[] {0, 30, 100, 30} /*timings*/, -1);
235
236        mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect };
237    }
238
239    public void systemReady() {
240        mIm = mContext.getSystemService(InputManager.class);
241        mSettingObserver = new SettingsObserver(mH);
242
243        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
244        mPowerManagerInternal.registerLowPowerModeObserver(
245                new PowerManagerInternal.LowPowerModeListener() {
246                    @Override
247                    public int getServiceType() {
248                        return ServiceType.VIBRATION;
249                    }
250
251                    @Override
252                    public void onLowPowerModeChanged(PowerSaveState result) {
253                        updateVibrators();
254                    }
255        });
256
257        mContext.getContentResolver().registerContentObserver(
258                Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
259                true, mSettingObserver, UserHandle.USER_ALL);
260
261        mContext.registerReceiver(new BroadcastReceiver() {
262            @Override
263            public void onReceive(Context context, Intent intent) {
264                updateVibrators();
265            }
266        }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
267
268        updateVibrators();
269    }
270
271    private final class SettingsObserver extends ContentObserver {
272        public SettingsObserver(Handler handler) {
273            super(handler);
274        }
275
276        @Override
277        public void onChange(boolean SelfChange) {
278            updateVibrators();
279        }
280    }
281
282    @Override // Binder call
283    public boolean hasVibrator() {
284        return doVibratorExists();
285    }
286
287    @Override // Binder call
288    public boolean hasAmplitudeControl() {
289        synchronized (mInputDeviceVibrators) {
290            // Input device vibrators don't support amplitude controls yet, but are still used over
291            // the system vibrator when connected.
292            return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty();
293        }
294    }
295
296    private void verifyIncomingUid(int uid) {
297        if (uid == Binder.getCallingUid()) {
298            return;
299        }
300        if (Binder.getCallingPid() == Process.myPid()) {
301            return;
302        }
303        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
304                Binder.getCallingPid(), Binder.getCallingUid(), null);
305    }
306
307    /**
308     * Validate the incoming VibrationEffect.
309     *
310     * We can't throw exceptions here since we might be called from some system_server component,
311     * which would bring the whole system down.
312     *
313     * @return whether the VibrationEffect is valid
314     */
315    private static boolean verifyVibrationEffect(VibrationEffect effect) {
316        if (effect == null) {
317            // Effect must not be null.
318            Slog.wtf(TAG, "effect must not be null");
319            return false;
320        }
321        try {
322            effect.validate();
323        } catch (Exception e) {
324            Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e);
325            return false;
326        }
327        return true;
328    }
329
330    private static long[] getLongIntArray(Resources r, int resid) {
331        int[] ar = r.getIntArray(resid);
332        if (ar == null) {
333            return null;
334        }
335        long[] out = new long[ar.length];
336        for (int i = 0; i < ar.length; i++) {
337            out[i] = ar[i];
338        }
339        return out;
340    }
341
342    @Override // Binder call
343    public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint,
344            IBinder token) {
345        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
346                != PackageManager.PERMISSION_GRANTED) {
347            throw new SecurityException("Requires VIBRATE permission");
348        }
349        if (token == null) {
350            Slog.e(TAG, "token must not be null");
351            return;
352        }
353        verifyIncomingUid(uid);
354        if (!verifyVibrationEffect(effect)) {
355            return;
356        }
357
358        // If our current vibration is longer than the new vibration and is the same amplitude,
359        // then just let the current one finish.
360        if (effect instanceof VibrationEffect.OneShot
361                && mCurrentVibration != null
362                && mCurrentVibration.mEffect instanceof VibrationEffect.OneShot) {
363            VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect;
364            VibrationEffect.OneShot currentOneShot =
365                    (VibrationEffect.OneShot) mCurrentVibration.mEffect;
366            if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming())
367                    && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) {
368                if (DEBUG) {
369                    Slog.e(TAG, "Ignoring incoming vibration in favor of current vibration");
370                }
371                return;
372            }
373        }
374
375        Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg);
376
377        // Only link against waveforms since they potentially don't have a finish if
378        // they're repeating. Let other effects just play out until they're done.
379        if (effect instanceof VibrationEffect.Waveform) {
380            try {
381                token.linkToDeath(vib, 0);
382            } catch (RemoteException e) {
383                return;
384            }
385        }
386
387
388        long ident = Binder.clearCallingIdentity();
389        try {
390            synchronized (mLock) {
391                doCancelVibrateLocked();
392                startVibrationLocked(vib);
393                addToPreviousVibrationsLocked(vib);
394            }
395        } finally {
396            Binder.restoreCallingIdentity(ident);
397        }
398    }
399
400    private void addToPreviousVibrationsLocked(Vibration vib) {
401        if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
402            mPreviousVibrations.removeFirst();
403        }
404        mPreviousVibrations.addLast(new VibrationInfo(
405                    vib.mStartTime, vib.mEffect, vib.mUsageHint, vib.mUid, vib.mOpPkg));
406    }
407
408    @Override // Binder call
409    public void cancelVibrate(IBinder token) {
410        mContext.enforceCallingOrSelfPermission(
411                android.Manifest.permission.VIBRATE,
412                "cancelVibrate");
413
414        synchronized (mLock) {
415            if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
416                if (DEBUG) {
417                    Slog.d(TAG, "Canceling vibration.");
418                }
419                long ident = Binder.clearCallingIdentity();
420                try {
421                    doCancelVibrateLocked();
422                } finally {
423                    Binder.restoreCallingIdentity(ident);
424                }
425            }
426        }
427    }
428
429    private final Runnable mVibrationEndRunnable = new Runnable() {
430        @Override
431        public void run() {
432            onVibrationFinished();
433        }
434    };
435
436    private void doCancelVibrateLocked() {
437        mH.removeCallbacks(mVibrationEndRunnable);
438        if (mThread != null) {
439            mThread.cancel();
440            mThread = null;
441        }
442        doVibratorOff();
443        reportFinishVibrationLocked();
444    }
445
446    // Callback for whenever the current vibration has finished played out
447    public void onVibrationFinished() {
448        if (DEBUG) {
449            Slog.e(TAG, "Vibration finished, cleaning up");
450        }
451        synchronized (mLock) {
452            // Make sure the vibration is really done. This also reports that the vibration is
453            // finished.
454            doCancelVibrateLocked();
455        }
456    }
457
458    private void startVibrationLocked(final Vibration vib) {
459        if (mLowPowerMode && vib.mUsageHint != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
460            if (DEBUG) {
461                Slog.e(TAG, "Vibrate ignored, low power mode");
462            }
463            return;
464        }
465
466        if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE &&
467                !shouldVibrateForRingtone()) {
468            if (DEBUG) {
469                Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones");
470            }
471            return;
472        }
473
474        final int mode = getAppOpMode(vib);
475        if (mode != AppOpsManager.MODE_ALLOWED) {
476            if (mode == AppOpsManager.MODE_ERRORED) {
477                // We might be getting calls from within system_server, so we don't actually want
478                // to throw a SecurityException here.
479                Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
480            }
481            return;
482        }
483        startVibrationInnerLocked(vib);
484    }
485
486    private void startVibrationInnerLocked(Vibration vib) {
487        mCurrentVibration = vib;
488        if (vib.mEffect instanceof VibrationEffect.OneShot) {
489            VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.mEffect;
490            doVibratorOn(oneShot.getTiming(), oneShot.getAmplitude(), vib.mUid, vib.mUsageHint);
491            mH.postDelayed(mVibrationEndRunnable, oneShot.getTiming());
492        } else if (vib.mEffect instanceof VibrationEffect.Waveform) {
493            // mThread better be null here. doCancelVibrate should always be
494            // called before startNextVibrationLocked or startVibrationLocked.
495            VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.mEffect;
496            mThread = new VibrateThread(waveform, vib.mUid, vib.mUsageHint);
497            mThread.start();
498        } else if (vib.mEffect instanceof VibrationEffect.Prebaked) {
499            long timeout = doVibratorPrebakedEffectLocked(vib);
500            if (timeout > 0) {
501                mH.postDelayed(mVibrationEndRunnable, timeout);
502            }
503        } else {
504            Slog.e(TAG, "Unknown vibration type, ignoring");
505        }
506    }
507
508    private boolean shouldVibrateForRingtone() {
509        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
510        int ringerMode = audioManager.getRingerModeInternal();
511        // "Also vibrate for calls" Setting in Sound
512        if (Settings.System.getInt(
513                mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) {
514            return ringerMode != AudioManager.RINGER_MODE_SILENT;
515        } else {
516            return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
517        }
518    }
519
520    private int getAppOpMode(Vibration vib) {
521        int mode;
522        try {
523            mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
524                    vib.mUsageHint, vib.mUid, vib.mOpPkg);
525            if (mode == AppOpsManager.MODE_ALLOWED) {
526                mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
527                    AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg);
528            }
529        } catch (RemoteException e) {
530            Slog.e(TAG, "Failed to get appop mode for vibration!", e);
531            mode = AppOpsManager.MODE_IGNORED;
532        }
533        return mode;
534    }
535
536    private void reportFinishVibrationLocked() {
537        if (mCurrentVibration != null) {
538            try {
539                mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
540                        AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
541                        mCurrentVibration.mOpPkg);
542            } catch (RemoteException e) { }
543            mCurrentVibration = null;
544        }
545    }
546
547    private void unlinkVibration(Vibration vib) {
548        if (vib.mEffect instanceof VibrationEffect.Waveform) {
549            vib.mToken.unlinkToDeath(vib, 0);
550        }
551    }
552
553    private void updateVibrators() {
554        synchronized (mLock) {
555            boolean devicesUpdated = updateInputDeviceVibratorsLocked();
556            boolean lowPowerModeUpdated = updateLowPowerModeLocked();
557
558            if (devicesUpdated || lowPowerModeUpdated) {
559                // If the state changes out from under us then just reset.
560                doCancelVibrateLocked();
561            }
562        }
563    }
564
565    private boolean updateInputDeviceVibratorsLocked() {
566        boolean changed = false;
567        boolean vibrateInputDevices = false;
568        try {
569            vibrateInputDevices = Settings.System.getIntForUser(
570                    mContext.getContentResolver(),
571                    Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
572        } catch (SettingNotFoundException snfe) {
573        }
574        if (vibrateInputDevices != mVibrateInputDevicesSetting) {
575            changed = true;
576            mVibrateInputDevicesSetting = vibrateInputDevices;
577        }
578
579        if (mVibrateInputDevicesSetting) {
580            if (!mInputDeviceListenerRegistered) {
581                mInputDeviceListenerRegistered = true;
582                mIm.registerInputDeviceListener(this, mH);
583            }
584        } else {
585            if (mInputDeviceListenerRegistered) {
586                mInputDeviceListenerRegistered = false;
587                mIm.unregisterInputDeviceListener(this);
588            }
589        }
590
591        mInputDeviceVibrators.clear();
592        if (mVibrateInputDevicesSetting) {
593            int[] ids = mIm.getInputDeviceIds();
594            for (int i = 0; i < ids.length; i++) {
595                InputDevice device = mIm.getInputDevice(ids[i]);
596                Vibrator vibrator = device.getVibrator();
597                if (vibrator.hasVibrator()) {
598                    mInputDeviceVibrators.add(vibrator);
599                }
600            }
601            return true;
602        }
603        return changed;
604    }
605
606    private boolean updateLowPowerModeLocked() {
607        boolean lowPowerMode = mPowerManagerInternal
608                .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled;
609        if (lowPowerMode != mLowPowerMode) {
610            mLowPowerMode = lowPowerMode;
611            return true;
612        }
613        return false;
614    }
615
616    @Override
617    public void onInputDeviceAdded(int deviceId) {
618        updateVibrators();
619    }
620
621    @Override
622    public void onInputDeviceChanged(int deviceId) {
623        updateVibrators();
624    }
625
626    @Override
627    public void onInputDeviceRemoved(int deviceId) {
628        updateVibrators();
629    }
630
631    private boolean doVibratorExists() {
632        // For now, we choose to ignore the presence of input devices that have vibrators
633        // when reporting whether the device has a vibrator.  Applications often use this
634        // information to decide whether to enable certain features so they expect the
635        // result of hasVibrator() to be constant.  For now, just report whether
636        // the device has a built-in vibrator.
637        //synchronized (mInputDeviceVibrators) {
638        //    return !mInputDeviceVibrators.isEmpty() || vibratorExists();
639        //}
640        return vibratorExists();
641    }
642
643    private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) {
644        synchronized (mInputDeviceVibrators) {
645            if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) {
646                amplitude = mDefaultVibrationAmplitude;
647            }
648            if (DEBUG) {
649                Slog.d(TAG, "Turning vibrator on for " + millis + " ms" +
650                        " with amplitude " + amplitude + ".");
651            }
652            noteVibratorOnLocked(uid, millis);
653            final int vibratorCount = mInputDeviceVibrators.size();
654            if (vibratorCount != 0) {
655                final AudioAttributes attributes =
656                        new AudioAttributes.Builder().setUsage(usageHint).build();
657                for (int i = 0; i < vibratorCount; i++) {
658                    mInputDeviceVibrators.get(i).vibrate(millis, attributes);
659                }
660            } else {
661                // Note: ordering is important here! Many haptic drivers will reset their amplitude
662                // when enabled, so we always have to enable frst, then set the amplitude.
663                vibratorOn(millis);
664                doVibratorSetAmplitude(amplitude);
665            }
666        }
667    }
668
669    private void doVibratorSetAmplitude(int amplitude) {
670        if (mSupportsAmplitudeControl) {
671            vibratorSetAmplitude(amplitude);
672        }
673    }
674
675    private void doVibratorOff() {
676        synchronized (mInputDeviceVibrators) {
677            if (DEBUG) {
678                Slog.d(TAG, "Turning vibrator off.");
679            }
680            noteVibratorOffLocked();
681            final int vibratorCount = mInputDeviceVibrators.size();
682            if (vibratorCount != 0) {
683                for (int i = 0; i < vibratorCount; i++) {
684                    mInputDeviceVibrators.get(i).cancel();
685                }
686            } else {
687                vibratorOff();
688            }
689        }
690    }
691
692    private long doVibratorPrebakedEffectLocked(Vibration vib) {
693        synchronized (mInputDeviceVibrators) {
694            VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.mEffect;
695            // Input devices don't support prebaked effect, so skip trying it with them.
696            final int vibratorCount = mInputDeviceVibrators.size();
697            if (vibratorCount == 0) {
698                long timeout = vibratorPerformEffect(prebaked.getId(), EffectStrength.MEDIUM);
699                if (timeout > 0) {
700                    noteVibratorOnLocked(vib.mUid, timeout);
701                    return timeout;
702                }
703            }
704            final int id = prebaked.getId();
705            if (id < 0 || id >= mFallbackEffects.length || mFallbackEffects[id] == null) {
706                Slog.w(TAG, "Failed to play prebaked effect, no fallback");
707                return 0;
708            }
709            VibrationEffect effect = mFallbackEffects[id];
710            Vibration fallbackVib =
711                    new Vibration(vib.mToken, effect, vib.mUsageHint, vib.mUid, vib.mOpPkg);
712            startVibrationInnerLocked(fallbackVib);
713        }
714        return 0;
715    }
716
717    private void noteVibratorOnLocked(int uid, long millis) {
718        try {
719            mBatteryStatsService.noteVibratorOn(uid, millis);
720            mCurVibUid = uid;
721        } catch (RemoteException e) {
722        }
723    }
724
725    private void noteVibratorOffLocked() {
726        if (mCurVibUid >= 0) {
727            try {
728                mBatteryStatsService.noteVibratorOff(mCurVibUid);
729            } catch (RemoteException e) { }
730            mCurVibUid = -1;
731        }
732    }
733
734    private class VibrateThread extends Thread {
735        private final VibrationEffect.Waveform mWaveform;
736        private final int mUid;
737        private final int mUsageHint;
738
739        private boolean mForceStop;
740
741        VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) {
742            mWaveform = waveform;
743            mUid = uid;
744            mUsageHint = usageHint;
745            mTmpWorkSource.set(uid);
746            mWakeLock.setWorkSource(mTmpWorkSource);
747        }
748
749        private long delayLocked(long duration) {
750            long durationRemaining = duration;
751            if (duration > 0) {
752                final long bedtime = duration + SystemClock.uptimeMillis();
753                do {
754                    try {
755                        this.wait(durationRemaining);
756                    }
757                    catch (InterruptedException e) { }
758                    if (mForceStop) {
759                        break;
760                    }
761                    durationRemaining = bedtime - SystemClock.uptimeMillis();
762                } while (durationRemaining > 0);
763                return duration - durationRemaining;
764            }
765            return 0;
766        }
767
768        public void run() {
769            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
770            mWakeLock.acquire();
771            try {
772                boolean finished = playWaveform();
773                if (finished) {
774                    onVibrationFinished();
775                }
776            } finally {
777                mWakeLock.release();
778            }
779        }
780
781        /**
782         * Play the waveform.
783         *
784         * @return true if it finished naturally, false otherwise (e.g. it was canceled).
785         */
786        public boolean playWaveform() {
787            synchronized (this) {
788                final long[] timings = mWaveform.getTimings();
789                final int[] amplitudes = mWaveform.getAmplitudes();
790                final int len = timings.length;
791                final int repeat = mWaveform.getRepeatIndex();
792
793                int index = 0;
794                long onDuration = 0;
795                while (!mForceStop) {
796                    if (index < len) {
797                        final int amplitude = amplitudes[index];
798                        final long duration = timings[index++];
799                        if (duration <= 0) {
800                            continue;
801                        }
802                        if (amplitude != 0) {
803                            if (onDuration <= 0) {
804                                // Telling the vibrator to start multiple times usually causes
805                                // effects to feel "choppy" because the motor resets at every on
806                                // command.  Instead we figure out how long our next "on" period is
807                                // going to be, tell the motor to stay on for the full duration,
808                                // and then wake up to change the amplitude at the appropriate
809                                // intervals.
810                                onDuration =
811                                        getTotalOnDuration(timings, amplitudes, index - 1, repeat);
812                                doVibratorOn(onDuration, amplitude, mUid, mUsageHint);
813                            } else {
814                                doVibratorSetAmplitude(amplitude);
815                            }
816                        }
817
818                        long waitTime = delayLocked(duration);
819                        if (amplitude != 0) {
820                            onDuration -= waitTime;
821                        }
822                    } else if (repeat < 0) {
823                        break;
824                    } else {
825                        index = repeat;
826                    }
827                }
828                return !mForceStop;
829            }
830        }
831
832        public void cancel() {
833            synchronized (this) {
834                mThread.mForceStop = true;
835                mThread.notify();
836            }
837        }
838
839        /**
840         * Get the duration the vibrator will be on starting at startIndex until the next time it's
841         * off.
842         */
843        private long getTotalOnDuration(
844                long[] timings, int[] amplitudes, int startIndex, int repeatIndex) {
845            int i = startIndex;
846            long timing = 0;
847            while(amplitudes[i] != 0) {
848                timing += timings[i++];
849                if (i >= timings.length) {
850                    if (repeatIndex >= 0) {
851                        i = repeatIndex;
852                    } else {
853                        break;
854                    }
855                }
856                if (i == startIndex) {
857                    return 1000;
858                }
859            }
860            return timing;
861        }
862    }
863
864    BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
865        @Override
866        public void onReceive(Context context, Intent intent) {
867            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
868                synchronized (mLock) {
869                    // When the system is entering a non-interactive state, we want
870                    // to cancel vibrations in case a misbehaving app has abandoned
871                    // them.  However it may happen that the system is currently playing
872                    // haptic feedback as part of the transition.  So we don't cancel
873                    // system vibrations.
874                    if (mCurrentVibration != null
875                            && !mCurrentVibration.isSystemHapticFeedback()) {
876                        doCancelVibrateLocked();
877                    }
878                }
879            }
880        }
881    };
882
883    @Override
884    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
885        if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return;
886
887        pw.println("Previous vibrations:");
888        synchronized (mLock) {
889            for (VibrationInfo info : mPreviousVibrations) {
890                pw.print("  ");
891                pw.println(info.toString());
892            }
893        }
894    }
895
896    @Override
897    public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err,
898            String[] args, ShellCallback callback, ResultReceiver resultReceiver)
899            throws RemoteException {
900        new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver);
901    }
902
903    private final class VibratorShellCommand extends ShellCommand {
904
905        private static final long MAX_VIBRATION_MS = 200;
906
907        private final IBinder mToken;
908
909        private VibratorShellCommand(IBinder token) {
910            mToken = token;
911        }
912
913        @Override
914        public int onCommand(String cmd) {
915            if ("vibrate".equals(cmd)) {
916                return runVibrate();
917            }
918            return handleDefaultCommands(cmd);
919        }
920
921        private int runVibrate() {
922            final long duration = Long.parseLong(getNextArgRequired());
923            if (duration > MAX_VIBRATION_MS) {
924                throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS);
925            }
926            String description = getNextArg();
927            if (description == null) {
928                description = "Shell command";
929            }
930
931            VibrationEffect effect =
932                    VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE);
933            vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN,
934                    mToken);
935            return 0;
936        }
937
938        @Override
939        public void onHelp() {
940            try (PrintWriter pw = getOutPrintWriter();) {
941                pw.println("Vibrator commands:");
942                pw.println("  help");
943                pw.println("    Prints this help text.");
944                pw.println("");
945                pw.println("  vibrate duration [description]");
946                pw.println("    Vibrates for duration milliseconds.");
947                pw.println("");
948            }
949        }
950    }
951
952}
953