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