VibratorService.java revision 8a9b22056b13477f59df934928c00c58b5871c95
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.os.Handler;
25import android.os.IVibratorService;
26import android.os.PowerManager;
27import android.os.Process;
28import android.os.RemoteException;
29import android.os.IBinder;
30import android.os.Binder;
31import android.os.SystemClock;
32import android.util.Slog;
33
34import java.util.LinkedList;
35import java.util.ListIterator;
36
37public class VibratorService extends IVibratorService.Stub {
38    private static final String TAG = "VibratorService";
39
40    private final LinkedList<Vibration> mVibrations;
41    private Vibration mCurrentVibration;
42
43    private class Vibration implements IBinder.DeathRecipient {
44        private final IBinder mToken;
45        private final long    mTimeout;
46        private final long    mStartTime;
47        private final long[]  mPattern;
48        private final int     mRepeat;
49
50        Vibration(IBinder token, long millis) {
51            this(token, millis, null, 0);
52        }
53
54        Vibration(IBinder token, long[] pattern, int repeat) {
55            this(token, 0, pattern, repeat);
56        }
57
58        private Vibration(IBinder token, long millis, long[] pattern,
59                int repeat) {
60            mToken = token;
61            mTimeout = millis;
62            mStartTime = SystemClock.uptimeMillis();
63            mPattern = pattern;
64            mRepeat = repeat;
65        }
66
67        public void binderDied() {
68            synchronized (mVibrations) {
69                mVibrations.remove(this);
70                if (this == mCurrentVibration) {
71                    doCancelVibrateLocked();
72                    startNextVibrationLocked();
73                }
74            }
75        }
76
77        public boolean hasLongerTimeout(long millis) {
78            if (mTimeout == 0) {
79                // This is a pattern, return false to play the simple
80                // vibration.
81                return false;
82            }
83            if ((mStartTime + mTimeout)
84                    < (SystemClock.uptimeMillis() + millis)) {
85                // If this vibration will end before the time passed in, let
86                // the new vibration play.
87                return false;
88            }
89            return true;
90        }
91    }
92
93    VibratorService(Context context) {
94        // Reset the hardware to a default state, in case this is a runtime
95        // restart instead of a fresh boot.
96        vibratorOff();
97
98        mContext = context;
99        PowerManager pm = (PowerManager)context.getSystemService(
100                Context.POWER_SERVICE);
101        mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
102        mWakeLock.setReferenceCounted(true);
103
104        mVibrations = new LinkedList<Vibration>();
105
106        IntentFilter filter = new IntentFilter();
107        filter.addAction(Intent.ACTION_SCREEN_OFF);
108        context.registerReceiver(mIntentReceiver, filter);
109    }
110
111    public void vibrate(long milliseconds, IBinder token) {
112        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
113                != PackageManager.PERMISSION_GRANTED) {
114            throw new SecurityException("Requires VIBRATE permission");
115        }
116        // We're running in the system server so we cannot crash. Check for a
117        // timeout of 0 or negative. This will ensure that a vibration has
118        // either a timeout of > 0 or a non-null pattern.
119        if (milliseconds <= 0 || (mCurrentVibration != null
120                && mCurrentVibration.hasLongerTimeout(milliseconds))) {
121            // Ignore this vibration since the current vibration will play for
122            // longer than milliseconds.
123            return;
124        }
125        Vibration vib = new Vibration(token, milliseconds);
126        synchronized (mVibrations) {
127            removeVibrationLocked(token);
128            doCancelVibrateLocked();
129            mCurrentVibration = vib;
130            startVibrationLocked(vib);
131        }
132    }
133
134    private boolean isAll0(long[] pattern) {
135        int N = pattern.length;
136        for (int i = 0; i < N; i++) {
137            if (pattern[i] != 0) {
138                return false;
139            }
140        }
141        return true;
142    }
143
144    public void vibratePattern(long[] pattern, int repeat, IBinder token) {
145        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
146                != PackageManager.PERMISSION_GRANTED) {
147            throw new SecurityException("Requires VIBRATE permission");
148        }
149        // so wakelock calls will succeed
150        long identity = Binder.clearCallingIdentity();
151        try {
152            if (false) {
153                String s = "";
154                int N = pattern.length;
155                for (int i=0; i<N; i++) {
156                    s += " " + pattern[i];
157                }
158                Slog.i(TAG, "vibrating with pattern: " + s);
159            }
160
161            // we're running in the server so we can't fail
162            if (pattern == null || pattern.length == 0
163                    || isAll0(pattern)
164                    || repeat >= pattern.length || token == null) {
165                return;
166            }
167
168            Vibration vib = new Vibration(token, pattern, repeat);
169            try {
170                token.linkToDeath(vib, 0);
171            } catch (RemoteException e) {
172                return;
173            }
174
175            synchronized (mVibrations) {
176                removeVibrationLocked(token);
177                doCancelVibrateLocked();
178                if (repeat >= 0) {
179                    mVibrations.addFirst(vib);
180                    startNextVibrationLocked();
181                } else {
182                    // A negative repeat means that this pattern is not meant
183                    // to repeat. Treat it like a simple vibration.
184                    mCurrentVibration = vib;
185                    startVibrationLocked(vib);
186                }
187            }
188        }
189        finally {
190            Binder.restoreCallingIdentity(identity);
191        }
192    }
193
194    public void cancelVibrate(IBinder token) {
195        mContext.enforceCallingOrSelfPermission(
196                android.Manifest.permission.VIBRATE,
197                "cancelVibrate");
198
199        // so wakelock calls will succeed
200        long identity = Binder.clearCallingIdentity();
201        try {
202            synchronized (mVibrations) {
203                final Vibration vib = removeVibrationLocked(token);
204                if (vib == mCurrentVibration) {
205                    doCancelVibrateLocked();
206                    startNextVibrationLocked();
207                }
208            }
209        }
210        finally {
211            Binder.restoreCallingIdentity(identity);
212        }
213    }
214
215    private final Runnable mVibrationRunnable = new Runnable() {
216        public void run() {
217            synchronized (mVibrations) {
218                doCancelVibrateLocked();
219                startNextVibrationLocked();
220            }
221        }
222    };
223
224    // Lock held on mVibrations
225    private void doCancelVibrateLocked() {
226        if (mThread != null) {
227            synchronized (mThread) {
228                mThread.mDone = true;
229                mThread.notify();
230            }
231            mThread = null;
232        }
233        vibratorOff();
234        mH.removeCallbacks(mVibrationRunnable);
235    }
236
237    // Lock held on mVibrations
238    private void startNextVibrationLocked() {
239        if (mVibrations.size() <= 0) {
240            return;
241        }
242        mCurrentVibration = mVibrations.getFirst();
243        startVibrationLocked(mCurrentVibration);
244    }
245
246    // Lock held on mVibrations
247    private void startVibrationLocked(final Vibration vib) {
248        if (vib.mTimeout != 0) {
249            vibratorOn(vib.mTimeout);
250            mH.postDelayed(mVibrationRunnable, vib.mTimeout);
251        } else {
252            // mThread better be null here. doCancelVibrate should always be
253            // called before startNextVibrationLocked or startVibrationLocked.
254            mThread = new VibrateThread(vib);
255            mThread.start();
256        }
257    }
258
259    // Lock held on mVibrations
260    private Vibration removeVibrationLocked(IBinder token) {
261        ListIterator<Vibration> iter = mVibrations.listIterator(0);
262        while (iter.hasNext()) {
263            Vibration vib = iter.next();
264            if (vib.mToken == token) {
265                iter.remove();
266                return vib;
267            }
268        }
269        // We might be looking for a simple vibration which is only stored in
270        // mCurrentVibration.
271        if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
272            return mCurrentVibration;
273        }
274        return null;
275    }
276
277    private class VibrateThread extends Thread {
278        final Vibration mVibration;
279        boolean mDone;
280
281        VibrateThread(Vibration vib) {
282            mVibration = vib;
283            mWakeLock.acquire();
284        }
285
286        private void delay(long duration) {
287            if (duration > 0) {
288                long bedtime = SystemClock.uptimeMillis();
289                do {
290                    try {
291                        this.wait(duration);
292                    }
293                    catch (InterruptedException e) {
294                    }
295                    if (mDone) {
296                        break;
297                    }
298                    duration = duration
299                            - SystemClock.uptimeMillis() - bedtime;
300                } while (duration > 0);
301            }
302        }
303
304        public void run() {
305            Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
306            synchronized (this) {
307                int index = 0;
308                long[] pattern = mVibration.mPattern;
309                int len = pattern.length;
310                int repeat = mVibration.mRepeat;
311                long duration = 0;
312
313                while (!mDone) {
314                    // add off-time duration to any accumulated on-time duration
315                    if (index < len) {
316                        duration += pattern[index++];
317                    }
318
319                    // sleep until it is time to start the vibrator
320                    delay(duration);
321                    if (mDone) {
322                        break;
323                    }
324
325                    if (index < len) {
326                        // read on-time duration and start the vibrator
327                        // duration is saved for delay() at top of loop
328                        duration = pattern[index++];
329                        if (duration > 0) {
330                            VibratorService.this.vibratorOn(duration);
331                        }
332                    } else {
333                        if (repeat < 0) {
334                            break;
335                        } else {
336                            index = repeat;
337                            duration = 0;
338                        }
339                    }
340                }
341                mWakeLock.release();
342            }
343            synchronized (mVibrations) {
344                if (mThread == this) {
345                    mThread = null;
346                }
347                if (!mDone) {
348                    // If this vibration finished naturally, start the next
349                    // vibration.
350                    mVibrations.remove(mVibration);
351                    startNextVibrationLocked();
352                }
353            }
354        }
355    };
356
357    BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
358        public void onReceive(Context context, Intent intent) {
359            if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
360                synchronized (mVibrations) {
361                    doCancelVibrateLocked();
362                    mVibrations.clear();
363                }
364            }
365        }
366    };
367
368    private Handler mH = new Handler();
369
370    private final Context mContext;
371    private final PowerManager.WakeLock mWakeLock;
372
373    volatile VibrateThread mThread;
374
375    native static void vibratorOn(long milliseconds);
376    native static void vibratorOff();
377}
378