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