VibratorService.java revision 4357e528208c301ccf733b074eb9a815997447c9
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.database.ContentObserver;
26import android.hardware.input.InputManager;
27import android.media.AudioManager;
28import android.os.BatteryStats;
29import android.os.Handler;
30import android.os.IVibratorService;
31import android.os.PowerManager;
32import android.os.PowerManagerInternal;
33import android.os.Process;
34import android.os.RemoteException;
35import android.os.IBinder;
36import android.os.Binder;
37import android.os.ServiceManager;
38import android.os.SystemClock;
39import android.os.UserHandle;
40import android.os.Vibrator;
41import android.os.WorkSource;
42import android.provider.Settings;
43import android.provider.Settings.SettingNotFoundException;
44import android.util.Slog;
45import android.view.InputDevice;
46import android.media.AudioAttributes;
47
48import com.android.internal.app.IAppOpsService;
49import com.android.internal.app.IBatteryStats;
50
51import java.io.FileDescriptor;
52import java.io.PrintWriter;
53import java.util.ArrayList;
54import java.util.Arrays;
55import java.util.Iterator;
56import java.util.LinkedList;
57import java.util.ListIterator;
58
59public class VibratorService extends IVibratorService.Stub
60        implements InputManager.InputDeviceListener {
61    private static final String TAG = "VibratorService";
62    private static final boolean DEBUG = false;
63    private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
64
65    private final LinkedList<Vibration> mVibrations;
66    private final LinkedList<VibrationInfo> mPreviousVibrations;
67    private final int mPreviousVibrationsLimit;
68    private Vibration mCurrentVibration;
69    private final WorkSource mTmpWorkSource = new WorkSource();
70    private final Handler mH = new Handler();
71
72    private final Context mContext;
73    private final PowerManager.WakeLock mWakeLock;
74    private final IAppOpsService mAppOpsService;
75    private final IBatteryStats mBatteryStatsService;
76    private PowerManagerInternal mPowerManagerInternal;
77    private InputManager mIm;
78
79    volatile VibrateThread mThread;
80
81    // mInputDeviceVibrators lock should be acquired after mVibrations lock, if both are
82    // to be acquired
83    private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>();
84    private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators
85    private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators
86
87    private int mCurVibUid = -1;
88    private boolean mLowPowerMode;
89    private SettingsObserver mSettingObserver;
90
91    native static boolean vibratorExists();
92    native static void vibratorInit();
93    native static void vibratorOn(long milliseconds);
94    native static void vibratorOff();
95
96    private class Vibration implements IBinder.DeathRecipient {
97        private final IBinder mToken;
98        private final long    mTimeout;
99        private final long    mStartTime;
100        private final long[]  mPattern;
101        private final int     mRepeat;
102        private final int     mUsageHint;
103        private final int     mUid;
104        private final String  mOpPkg;
105
106        Vibration(IBinder token, long millis, int usageHint, int uid, String opPkg) {
107            this(token, millis, null, 0, usageHint, uid, opPkg);
108        }
109
110        Vibration(IBinder token, long[] pattern, int repeat, int usageHint, int uid,
111                String opPkg) {
112            this(token, 0, pattern, repeat, usageHint, uid, opPkg);
113        }
114
115        private Vibration(IBinder token, long millis, long[] pattern,
116                int repeat, int usageHint, int uid, String opPkg) {
117            mToken = token;
118            mTimeout = millis;
119            mStartTime = SystemClock.uptimeMillis();
120            mPattern = pattern;
121            mRepeat = repeat;
122            mUsageHint = usageHint;
123            mUid = uid;
124            mOpPkg = opPkg;
125        }
126
127        public void binderDied() {
128            synchronized (mVibrations) {
129                mVibrations.remove(this);
130                if (this == mCurrentVibration) {
131                    doCancelVibrateLocked();
132                    startNextVibrationLocked();
133                }
134            }
135        }
136
137        public boolean hasLongerTimeout(long millis) {
138            if (mTimeout == 0) {
139                // This is a pattern, return false to play the simple
140                // vibration.
141                return false;
142            }
143            if ((mStartTime + mTimeout)
144                    < (SystemClock.uptimeMillis() + millis)) {
145                // If this vibration will end before the time passed in, let
146                // the new vibration play.
147                return false;
148            }
149            return true;
150        }
151
152        public boolean isSystemHapticFeedback() {
153            return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg))
154                    && mRepeat < 0;
155        }
156    }
157
158    private static class VibrationInfo {
159        long timeout;
160        long startTime;
161        long[] pattern;
162        int repeat;
163        int usageHint;
164        int uid;
165        String opPkg;
166
167        public VibrationInfo(long timeout, long startTime, long[] pattern, int repeat,
168                int usageHint, int uid, String opPkg) {
169            this.timeout = timeout;
170            this.startTime = startTime;
171            this.pattern = pattern;
172            this.repeat = repeat;
173            this.usageHint = usageHint;
174            this.uid = uid;
175            this.opPkg = opPkg;
176        }
177
178        @Override
179        public String toString() {
180            return new StringBuilder()
181                    .append("timeout: ")
182                    .append(timeout)
183                    .append(", startTime: ")
184                    .append(startTime)
185                    .append(", pattern: ")
186                    .append(Arrays.toString(pattern))
187                    .append(", repeat: ")
188                    .append(repeat)
189                    .append(", usageHint: ")
190                    .append(usageHint)
191                    .append(", uid: ")
192                    .append(uid)
193                    .append(", opPkg: ")
194                    .append(opPkg)
195                    .toString();
196        }
197    }
198
199    VibratorService(Context context) {
200        vibratorInit();
201        // Reset the hardware to a default state, in case this is a runtime
202        // restart instead of a fresh boot.
203        vibratorOff();
204
205        mContext = context;
206        PowerManager pm = (PowerManager)context.getSystemService(
207                Context.POWER_SERVICE);
208        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
209        mWakeLock.setReferenceCounted(true);
210
211        mAppOpsService = IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE));
212        mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService(
213                BatteryStats.SERVICE_NAME));
214
215        mPreviousVibrationsLimit = mContext.getResources().getInteger(
216                com.android.internal.R.integer.config_previousVibrationsDumpLimit);
217
218        mVibrations = new LinkedList<>();
219        mPreviousVibrations = new LinkedList<>();
220
221        IntentFilter filter = new IntentFilter();
222        filter.addAction(Intent.ACTION_SCREEN_OFF);
223        context.registerReceiver(mIntentReceiver, filter);
224    }
225
226    public void systemReady() {
227        mIm = mContext.getSystemService(InputManager.class);
228        mSettingObserver = new SettingsObserver(mH);
229
230        mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class);
231        mPowerManagerInternal.registerLowPowerModeObserver(
232                new PowerManagerInternal.LowPowerModeListener() {
233            @Override
234            public void onLowPowerModeChanged(boolean enabled) {
235                updateInputDeviceVibrators();
236            }
237        });
238
239        mContext.getContentResolver().registerContentObserver(
240                Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES),
241                true, mSettingObserver, UserHandle.USER_ALL);
242
243        mContext.registerReceiver(new BroadcastReceiver() {
244            @Override
245            public void onReceive(Context context, Intent intent) {
246                updateInputDeviceVibrators();
247            }
248        }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH);
249
250        updateInputDeviceVibrators();
251    }
252
253    private final class SettingsObserver extends ContentObserver {
254        public SettingsObserver(Handler handler) {
255            super(handler);
256        }
257
258        @Override
259        public void onChange(boolean SelfChange) {
260            updateInputDeviceVibrators();
261        }
262    }
263
264    @Override // Binder call
265    public boolean hasVibrator() {
266        return doVibratorExists();
267    }
268
269    private void verifyIncomingUid(int uid) {
270        if (uid == Binder.getCallingUid()) {
271            return;
272        }
273        if (Binder.getCallingPid() == Process.myPid()) {
274            return;
275        }
276        mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS,
277                Binder.getCallingPid(), Binder.getCallingUid(), null);
278    }
279
280    @Override // Binder call
281    public void vibrate(int uid, String opPkg, long milliseconds, int usageHint,
282            IBinder token) {
283        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
284                != PackageManager.PERMISSION_GRANTED) {
285            throw new SecurityException("Requires VIBRATE permission");
286        }
287        verifyIncomingUid(uid);
288        // We're running in the system server so we cannot crash. Check for a
289        // timeout of 0 or negative. This will ensure that a vibration has
290        // either a timeout of > 0 or a non-null pattern.
291        if (milliseconds <= 0 || (mCurrentVibration != null
292                && mCurrentVibration.hasLongerTimeout(milliseconds))) {
293            // Ignore this vibration since the current vibration will play for
294            // longer than milliseconds.
295            return;
296        }
297
298        if (DEBUG) {
299            Slog.d(TAG, "Vibrating for " + milliseconds + " ms.");
300        }
301
302        Vibration vib = new Vibration(token, milliseconds, usageHint, uid, opPkg);
303
304        final long ident = Binder.clearCallingIdentity();
305        try {
306            synchronized (mVibrations) {
307                removeVibrationLocked(token);
308                doCancelVibrateLocked();
309                addToPreviousVibrationsLocked(vib);
310                startVibrationLocked(vib);
311            }
312        } finally {
313            Binder.restoreCallingIdentity(ident);
314        }
315    }
316
317    private boolean isAll0(long[] pattern) {
318        int N = pattern.length;
319        for (int i = 0; i < N; i++) {
320            if (pattern[i] != 0) {
321                return false;
322            }
323        }
324        return true;
325    }
326
327    @Override // Binder call
328    public void vibratePattern(int uid, String packageName, long[] pattern, int repeat,
329            int usageHint, IBinder token) {
330        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
331                != PackageManager.PERMISSION_GRANTED) {
332            throw new SecurityException("Requires VIBRATE permission");
333        }
334        verifyIncomingUid(uid);
335        // so wakelock calls will succeed
336        long identity = Binder.clearCallingIdentity();
337        try {
338            if (DEBUG) {
339                String s = "";
340                int N = pattern.length;
341                for (int i=0; i<N; i++) {
342                    s += " " + pattern[i];
343                }
344                Slog.d(TAG, "Vibrating with pattern:" + s);
345            }
346
347            // we're running in the server so we can't fail
348            if (pattern == null || pattern.length == 0
349                    || isAll0(pattern)
350                    || repeat >= pattern.length || token == null) {
351                return;
352            }
353
354            Vibration vib = new Vibration(token, pattern, repeat, usageHint, uid, packageName);
355            try {
356                token.linkToDeath(vib, 0);
357            } catch (RemoteException e) {
358                return;
359            }
360
361            synchronized (mVibrations) {
362                removeVibrationLocked(token);
363                doCancelVibrateLocked();
364                if (repeat >= 0) {
365                    mVibrations.addFirst(vib);
366                    startNextVibrationLocked();
367                } else {
368                    // A negative repeat means that this pattern is not meant
369                    // to repeat. Treat it like a simple vibration.
370                    startVibrationLocked(vib);
371                }
372                addToPreviousVibrationsLocked(vib);
373            }
374        }
375        finally {
376            Binder.restoreCallingIdentity(identity);
377        }
378    }
379
380    private void addToPreviousVibrationsLocked(Vibration vib) {
381        if (mPreviousVibrations.size() > mPreviousVibrationsLimit) {
382            mPreviousVibrations.removeFirst();
383        }
384        mPreviousVibrations.addLast(new VibratorService.VibrationInfo(vib.mTimeout, vib.mStartTime,
385                vib.mPattern, vib.mRepeat, vib.mUsageHint, vib.mUid, vib.mOpPkg));
386    }
387
388    @Override // Binder call
389    public void cancelVibrate(IBinder token) {
390        mContext.enforceCallingOrSelfPermission(
391                android.Manifest.permission.VIBRATE,
392                "cancelVibrate");
393
394        // so wakelock calls will succeed
395        long identity = Binder.clearCallingIdentity();
396        try {
397            synchronized (mVibrations) {
398                final Vibration vib = removeVibrationLocked(token);
399                if (vib == mCurrentVibration) {
400                    if (DEBUG) {
401                        Slog.d(TAG, "Canceling vibration.");
402                    }
403                    doCancelVibrateLocked();
404                    startNextVibrationLocked();
405                }
406            }
407        }
408        finally {
409            Binder.restoreCallingIdentity(identity);
410        }
411    }
412
413    private final Runnable mVibrationRunnable = new Runnable() {
414        @Override
415        public void run() {
416            synchronized (mVibrations) {
417                doCancelVibrateLocked();
418                startNextVibrationLocked();
419            }
420        }
421    };
422
423    // Lock held on mVibrations
424    private void doCancelVibrateLocked() {
425        if (mThread != null) {
426            synchronized (mThread) {
427                mThread.mDone = true;
428                mThread.notify();
429            }
430            mThread = null;
431        }
432        doVibratorOff();
433        mH.removeCallbacks(mVibrationRunnable);
434        reportFinishVibrationLocked();
435    }
436
437    // Lock held on mVibrations
438    private void startNextVibrationLocked() {
439        if (mVibrations.size() <= 0) {
440            reportFinishVibrationLocked();
441            mCurrentVibration = null;
442            return;
443        }
444        startVibrationLocked(mVibrations.getFirst());
445    }
446
447    // Lock held on mVibrations
448    private void startVibrationLocked(final Vibration vib) {
449        try {
450            if (mLowPowerMode
451                    && vib.mUsageHint != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) {
452                return;
453            }
454
455            if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE &&
456                    !shouldVibrateForRingtone()) {
457                return;
458            }
459
460            int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
461                    vib.mUsageHint, vib.mUid, vib.mOpPkg);
462            if (mode == AppOpsManager.MODE_ALLOWED) {
463                mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
464                    AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg);
465            }
466            if (mode == AppOpsManager.MODE_ALLOWED) {
467                mCurrentVibration = vib;
468            } else {
469                if (mode == AppOpsManager.MODE_ERRORED) {
470                    Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
471                }
472                mH.post(mVibrationRunnable);
473                return;
474            }
475        } catch (RemoteException e) {
476        }
477        if (vib.mTimeout != 0) {
478            doVibratorOn(vib.mTimeout, vib.mUid, vib.mUsageHint);
479            mH.postDelayed(mVibrationRunnable, vib.mTimeout);
480        } else {
481            // mThread better be null here. doCancelVibrate should always be
482            // called before startNextVibrationLocked or startVibrationLocked.
483            mThread = new VibrateThread(vib);
484            mThread.start();
485        }
486    }
487
488    private boolean shouldVibrateForRingtone() {
489        AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
490        int ringerMode = audioManager.getRingerMode();
491        // "Also vibrate for calls" Setting in Sound
492        if (Settings.System.getInt(
493                mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) {
494            return ringerMode != AudioManager.RINGER_MODE_SILENT;
495        } else {
496            return ringerMode == AudioManager.RINGER_MODE_VIBRATE;
497        }
498    }
499
500    private void reportFinishVibrationLocked() {
501        if (mCurrentVibration != null) {
502            try {
503                mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
504                        AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
505                        mCurrentVibration.mOpPkg);
506            } catch (RemoteException e) {
507            }
508            mCurrentVibration = null;
509        }
510    }
511
512    // Lock held on mVibrations
513    private Vibration removeVibrationLocked(IBinder token) {
514        ListIterator<Vibration> iter = mVibrations.listIterator(0);
515        while (iter.hasNext()) {
516            Vibration vib = iter.next();
517            if (vib.mToken == token) {
518                iter.remove();
519                unlinkVibration(vib);
520                return vib;
521            }
522        }
523        // We might be looking for a simple vibration which is only stored in
524        // mCurrentVibration.
525        if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
526            unlinkVibration(mCurrentVibration);
527            return mCurrentVibration;
528        }
529        return null;
530    }
531
532    private void unlinkVibration(Vibration vib) {
533        if (vib.mPattern != null) {
534            // If Vibration object has a pattern,
535            // the Vibration object has also been linkedToDeath.
536            vib.mToken.unlinkToDeath(vib, 0);
537        }
538    }
539
540    private void updateInputDeviceVibrators() {
541        synchronized (mVibrations) {
542            doCancelVibrateLocked();
543
544            synchronized (mInputDeviceVibrators) {
545                mVibrateInputDevicesSetting = false;
546                try {
547                    mVibrateInputDevicesSetting = Settings.System.getIntForUser(
548                            mContext.getContentResolver(),
549                            Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
550                } catch (SettingNotFoundException snfe) {
551                }
552
553                mLowPowerMode = mPowerManagerInternal.getLowPowerModeEnabled();
554
555                if (mVibrateInputDevicesSetting) {
556                    if (!mInputDeviceListenerRegistered) {
557                        mInputDeviceListenerRegistered = true;
558                        mIm.registerInputDeviceListener(this, mH);
559                    }
560                } else {
561                    if (mInputDeviceListenerRegistered) {
562                        mInputDeviceListenerRegistered = false;
563                        mIm.unregisterInputDeviceListener(this);
564                    }
565                }
566
567                mInputDeviceVibrators.clear();
568                if (mVibrateInputDevicesSetting) {
569                    int[] ids = mIm.getInputDeviceIds();
570                    for (int i = 0; i < ids.length; i++) {
571                        InputDevice device = mIm.getInputDevice(ids[i]);
572                        Vibrator vibrator = device.getVibrator();
573                        if (vibrator.hasVibrator()) {
574                            mInputDeviceVibrators.add(vibrator);
575                        }
576                    }
577                }
578            }
579
580            startNextVibrationLocked();
581        }
582    }
583
584    @Override
585    public void onInputDeviceAdded(int deviceId) {
586        updateInputDeviceVibrators();
587    }
588
589    @Override
590    public void onInputDeviceChanged(int deviceId) {
591        updateInputDeviceVibrators();
592    }
593
594    @Override
595    public void onInputDeviceRemoved(int deviceId) {
596        updateInputDeviceVibrators();
597    }
598
599    private boolean doVibratorExists() {
600        // For now, we choose to ignore the presence of input devices that have vibrators
601        // when reporting whether the device has a vibrator.  Applications often use this
602        // information to decide whether to enable certain features so they expect the
603        // result of hasVibrator() to be constant.  For now, just report whether
604        // the device has a built-in vibrator.
605        //synchronized (mInputDeviceVibrators) {
606        //    return !mInputDeviceVibrators.isEmpty() || vibratorExists();
607        //}
608        return vibratorExists();
609    }
610
611    private void doVibratorOn(long millis, int uid, int usageHint) {
612        synchronized (mInputDeviceVibrators) {
613            if (DEBUG) {
614                Slog.d(TAG, "Turning vibrator on for " + millis + " ms.");
615            }
616            try {
617                mBatteryStatsService.noteVibratorOn(uid, millis);
618                mCurVibUid = uid;
619            } catch (RemoteException e) {
620            }
621            final int vibratorCount = mInputDeviceVibrators.size();
622            if (vibratorCount != 0) {
623                final AudioAttributes attributes = new AudioAttributes.Builder().setUsage(usageHint)
624                        .build();
625                for (int i = 0; i < vibratorCount; i++) {
626                    mInputDeviceVibrators.get(i).vibrate(millis, attributes);
627                }
628            } else {
629                vibratorOn(millis);
630            }
631        }
632    }
633
634    private void doVibratorOff() {
635        synchronized (mInputDeviceVibrators) {
636            if (DEBUG) {
637                Slog.d(TAG, "Turning vibrator off.");
638            }
639            if (mCurVibUid >= 0) {
640                try {
641                    mBatteryStatsService.noteVibratorOff(mCurVibUid);
642                } catch (RemoteException e) {
643                }
644                mCurVibUid = -1;
645            }
646            final int vibratorCount = mInputDeviceVibrators.size();
647            if (vibratorCount != 0) {
648                for (int i = 0; i < vibratorCount; i++) {
649                    mInputDeviceVibrators.get(i).cancel();
650                }
651            } else {
652                vibratorOff();
653            }
654        }
655    }
656
657    private class VibrateThread extends Thread {
658        final Vibration mVibration;
659        boolean mDone;
660
661        VibrateThread(Vibration vib) {
662            mVibration = vib;
663            mTmpWorkSource.set(vib.mUid);
664            mWakeLock.setWorkSource(mTmpWorkSource);
665            mWakeLock.acquire();
666        }
667
668        private void delay(long duration) {
669            if (duration > 0) {
670                long bedtime = duration + SystemClock.uptimeMillis();
671                do {
672                    try {
673                        this.wait(duration);
674                    }
675                    catch (InterruptedException e) {
676                    }
677                    if (mDone) {
678                        break;
679                    }
680                    duration = bedtime - SystemClock.uptimeMillis();
681                } while (duration > 0);
682            }
683        }
684
685        public void run() {
686            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
687            synchronized (this) {
688                final long[] pattern = mVibration.mPattern;
689                final int len = pattern.length;
690                final int repeat = mVibration.mRepeat;
691                final int uid = mVibration.mUid;
692                final int usageHint = mVibration.mUsageHint;
693                int index = 0;
694                long duration = 0;
695
696                while (!mDone) {
697                    // add off-time duration to any accumulated on-time duration
698                    if (index < len) {
699                        duration += pattern[index++];
700                    }
701
702                    // sleep until it is time to start the vibrator
703                    delay(duration);
704                    if (mDone) {
705                        break;
706                    }
707
708                    if (index < len) {
709                        // read on-time duration and start the vibrator
710                        // duration is saved for delay() at top of loop
711                        duration = pattern[index++];
712                        if (duration > 0) {
713                            VibratorService.this.doVibratorOn(duration, uid, usageHint);
714                        }
715                    } else {
716                        if (repeat < 0) {
717                            break;
718                        } else {
719                            index = repeat;
720                            duration = 0;
721                        }
722                    }
723                }
724                mWakeLock.release();
725            }
726            synchronized (mVibrations) {
727                if (mThread == this) {
728                    mThread = null;
729                }
730                if (!mDone) {
731                    // If this vibration finished naturally, start the next
732                    // vibration.
733                    unlinkVibration(mVibration);
734                    startNextVibrationLocked();
735                }
736            }
737        }
738    }
739
740    BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
741        @Override
742        public void onReceive(Context context, Intent intent) {
743            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
744                synchronized (mVibrations) {
745                    // When the system is entering a non-interactive state, we want
746                    // to cancel vibrations in case a misbehaving app has abandoned
747                    // them.  However it may happen that the system is currently playing
748                    // haptic feedback as part of the transition.  So we don't cancel
749                    // system vibrations.
750                    if (mCurrentVibration != null
751                            && !mCurrentVibration.isSystemHapticFeedback()) {
752                        doCancelVibrateLocked();
753                    }
754
755                    // Clear all remaining vibrations.
756                    Iterator<Vibration> it = mVibrations.iterator();
757                    while (it.hasNext()) {
758                        Vibration vibration = it.next();
759                        if (vibration != mCurrentVibration) {
760                            unlinkVibration(vibration);
761                            it.remove();
762                        }
763                    }
764                }
765            }
766        }
767    };
768
769    @Override
770    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
771        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
772                != PackageManager.PERMISSION_GRANTED) {
773
774            pw.println("Permission Denial: can't dump vibrator service from from pid="
775                    + Binder.getCallingPid()
776                    + ", uid=" + Binder.getCallingUid());
777            return;
778        }
779        pw.println("Previous vibrations:");
780        synchronized (mVibrations) {
781            for (VibrationInfo info : mPreviousVibrations) {
782                pw.print("  ");
783                pw.println(info.toString());
784            }
785        }
786    }
787}
788