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