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