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