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