VibratorService.java revision eb94fa7975b1e8742f3b00cec6bd4f9d6b329e3a
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.AudioManager;
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     mStreamHint;
94        private final int     mUid;
95        private final String  mOpPkg;
96
97        Vibration(IBinder token, long millis, int streamHint, int uid, String opPkg) {
98            this(token, millis, null, 0, streamHint, uid, opPkg);
99        }
100
101        Vibration(IBinder token, long[] pattern, int repeat, int streamHint, int uid,
102                String opPkg) {
103            this(token, 0, pattern, repeat, streamHint, uid, opPkg);
104        }
105
106        private Vibration(IBinder token, long millis, long[] pattern,
107                int repeat, int streamHint, int uid, String opPkg) {
108            mToken = token;
109            mTimeout = millis;
110            mStartTime = SystemClock.uptimeMillis();
111            mPattern = pattern;
112            mRepeat = repeat;
113            mStreamHint = streamHint;
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 streamHint,
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, streamHint, 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 streamHint, 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, streamHint, 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 && vib.mStreamHint != AudioManager.STREAM_RING) {
376                return;
377            }
378
379            int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE,
380                    vib.mStreamHint, vib.mUid, vib.mOpPkg);
381            if (mode == AppOpsManager.MODE_ALLOWED) {
382                mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService),
383                    AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg);
384            }
385            if (mode != AppOpsManager.MODE_ALLOWED) {
386                if (mode == AppOpsManager.MODE_ERRORED) {
387                    Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid);
388                }
389                mH.post(mVibrationRunnable);
390                return;
391            }
392        } catch (RemoteException e) {
393        }
394        if (vib.mTimeout != 0) {
395            doVibratorOn(vib.mTimeout, vib.mUid, vib.mStreamHint);
396            mH.postDelayed(mVibrationRunnable, vib.mTimeout);
397        } else {
398            // mThread better be null here. doCancelVibrate should always be
399            // called before startNextVibrationLocked or startVibrationLocked.
400            mThread = new VibrateThread(vib);
401            mThread.start();
402        }
403    }
404
405    private void reportFinishVibrationLocked() {
406        if (mCurrentVibration != null) {
407            try {
408                mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService),
409                        AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid,
410                        mCurrentVibration.mOpPkg);
411            } catch (RemoteException e) {
412            }
413            mCurrentVibration = null;
414        }
415    }
416
417    // Lock held on mVibrations
418    private Vibration removeVibrationLocked(IBinder token) {
419        ListIterator<Vibration> iter = mVibrations.listIterator(0);
420        while (iter.hasNext()) {
421            Vibration vib = iter.next();
422            if (vib.mToken == token) {
423                iter.remove();
424                unlinkVibration(vib);
425                return vib;
426            }
427        }
428        // We might be looking for a simple vibration which is only stored in
429        // mCurrentVibration.
430        if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
431            unlinkVibration(mCurrentVibration);
432            return mCurrentVibration;
433        }
434        return null;
435    }
436
437    private void unlinkVibration(Vibration vib) {
438        if (vib.mPattern != null) {
439            // If Vibration object has a pattern,
440            // the Vibration object has also been linkedToDeath.
441            vib.mToken.unlinkToDeath(vib, 0);
442        }
443    }
444
445    private void updateInputDeviceVibrators() {
446        synchronized (mVibrations) {
447            doCancelVibrateLocked();
448
449            synchronized (mInputDeviceVibrators) {
450                mVibrateInputDevicesSetting = false;
451                try {
452                    mVibrateInputDevicesSetting = Settings.System.getIntForUser(
453                            mContext.getContentResolver(),
454                            Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0;
455                } catch (SettingNotFoundException snfe) {
456                }
457
458                mLowPowerMode = mPowerManagerInternal.getLowPowerModeEnabled();
459
460                if (mVibrateInputDevicesSetting) {
461                    if (!mInputDeviceListenerRegistered) {
462                        mInputDeviceListenerRegistered = true;
463                        mIm.registerInputDeviceListener(this, mH);
464                    }
465                } else {
466                    if (mInputDeviceListenerRegistered) {
467                        mInputDeviceListenerRegistered = false;
468                        mIm.unregisterInputDeviceListener(this);
469                    }
470                }
471
472                mInputDeviceVibrators.clear();
473                if (mVibrateInputDevicesSetting) {
474                    int[] ids = mIm.getInputDeviceIds();
475                    for (int i = 0; i < ids.length; i++) {
476                        InputDevice device = mIm.getInputDevice(ids[i]);
477                        Vibrator vibrator = device.getVibrator();
478                        if (vibrator.hasVibrator()) {
479                            mInputDeviceVibrators.add(vibrator);
480                        }
481                    }
482                }
483            }
484
485            startNextVibrationLocked();
486        }
487    }
488
489    @Override
490    public void onInputDeviceAdded(int deviceId) {
491        updateInputDeviceVibrators();
492    }
493
494    @Override
495    public void onInputDeviceChanged(int deviceId) {
496        updateInputDeviceVibrators();
497    }
498
499    @Override
500    public void onInputDeviceRemoved(int deviceId) {
501        updateInputDeviceVibrators();
502    }
503
504    private boolean doVibratorExists() {
505        // For now, we choose to ignore the presence of input devices that have vibrators
506        // when reporting whether the device has a vibrator.  Applications often use this
507        // information to decide whether to enable certain features so they expect the
508        // result of hasVibrator() to be constant.  For now, just report whether
509        // the device has a built-in vibrator.
510        //synchronized (mInputDeviceVibrators) {
511        //    return !mInputDeviceVibrators.isEmpty() || vibratorExists();
512        //}
513        return vibratorExists();
514    }
515
516    private void doVibratorOn(long millis, int uid, int streamHint) {
517        synchronized (mInputDeviceVibrators) {
518            try {
519                mBatteryStatsService.noteVibratorOn(uid, millis);
520                mCurVibUid = uid;
521            } catch (RemoteException e) {
522            }
523            final int vibratorCount = mInputDeviceVibrators.size();
524            if (vibratorCount != 0) {
525                for (int i = 0; i < vibratorCount; i++) {
526                    mInputDeviceVibrators.get(i).vibrate(millis, streamHint);
527                }
528            } else {
529                vibratorOn(millis);
530            }
531        }
532    }
533
534    private void doVibratorOff() {
535        synchronized (mInputDeviceVibrators) {
536            if (mCurVibUid >= 0) {
537                try {
538                    mBatteryStatsService.noteVibratorOff(mCurVibUid);
539                } catch (RemoteException e) {
540                }
541                mCurVibUid = -1;
542            }
543            final int vibratorCount = mInputDeviceVibrators.size();
544            if (vibratorCount != 0) {
545                for (int i = 0; i < vibratorCount; i++) {
546                    mInputDeviceVibrators.get(i).cancel();
547                }
548            } else {
549                vibratorOff();
550            }
551        }
552    }
553
554    private class VibrateThread extends Thread {
555        final Vibration mVibration;
556        boolean mDone;
557
558        VibrateThread(Vibration vib) {
559            mVibration = vib;
560            mTmpWorkSource.set(vib.mUid);
561            mWakeLock.setWorkSource(mTmpWorkSource);
562            mWakeLock.acquire();
563        }
564
565        private void delay(long duration) {
566            if (duration > 0) {
567                long bedtime = duration + SystemClock.uptimeMillis();
568                do {
569                    try {
570                        this.wait(duration);
571                    }
572                    catch (InterruptedException e) {
573                    }
574                    if (mDone) {
575                        break;
576                    }
577                    duration = bedtime - SystemClock.uptimeMillis();
578                } while (duration > 0);
579            }
580        }
581
582        public void run() {
583            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
584            synchronized (this) {
585                final long[] pattern = mVibration.mPattern;
586                final int len = pattern.length;
587                final int repeat = mVibration.mRepeat;
588                final int uid = mVibration.mUid;
589                final int streamHint = mVibration.mStreamHint;
590                int index = 0;
591                long duration = 0;
592
593                while (!mDone) {
594                    // add off-time duration to any accumulated on-time duration
595                    if (index < len) {
596                        duration += pattern[index++];
597                    }
598
599                    // sleep until it is time to start the vibrator
600                    delay(duration);
601                    if (mDone) {
602                        break;
603                    }
604
605                    if (index < len) {
606                        // read on-time duration and start the vibrator
607                        // duration is saved for delay() at top of loop
608                        duration = pattern[index++];
609                        if (duration > 0) {
610                            VibratorService.this.doVibratorOn(duration, uid, streamHint);
611                        }
612                    } else {
613                        if (repeat < 0) {
614                            break;
615                        } else {
616                            index = repeat;
617                            duration = 0;
618                        }
619                    }
620                }
621                mWakeLock.release();
622            }
623            synchronized (mVibrations) {
624                if (mThread == this) {
625                    mThread = null;
626                }
627                if (!mDone) {
628                    // If this vibration finished naturally, start the next
629                    // vibration.
630                    mVibrations.remove(mVibration);
631                    unlinkVibration(mVibration);
632                    startNextVibrationLocked();
633                }
634            }
635        }
636    }
637
638    BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
639        @Override
640        public void onReceive(Context context, Intent intent) {
641            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
642                synchronized (mVibrations) {
643                    // When the system is entering a non-interactive state, we want
644                    // to cancel vibrations in case a misbehaving app has abandoned
645                    // them.  However it may happen that the system is currently playing
646                    // haptic feedback as part of the transition.  So we don't cancel
647                    // system vibrations.
648                    if (mCurrentVibration != null
649                            && !mCurrentVibration.isSystemHapticFeedback()) {
650                        doCancelVibrateLocked();
651                    }
652
653                    // Clear all remaining vibrations.
654                    Iterator<Vibration> it = mVibrations.iterator();
655                    while (it.hasNext()) {
656                        Vibration vibration = it.next();
657                        if (vibration != mCurrentVibration) {
658                            unlinkVibration(vibration);
659                            it.remove();
660                        }
661                    }
662                }
663            }
664        }
665    };
666}
667