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