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