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