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