blob: 2e7e3e18dcddd48b3951b4baaa7b35cd6844f589 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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;
Joe Onorato95e4f702009-03-24 19:29:09 -070024import android.os.Handler;
Mike Lockwood3a322132009-11-24 00:30:52 -050025import android.os.IVibratorService;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.os.PowerManager;
27import android.os.Process;
28import android.os.RemoteException;
29import android.os.IBinder;
30import android.os.Binder;
31import android.os.SystemClock;
Joe Onorato8a9b2202010-02-26 18:56:32 -080032import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033
Patrick Scott18dd5f02009-07-02 11:31:12 -040034import java.util.LinkedList;
35import java.util.ListIterator;
36
Mike Lockwood3a322132009-11-24 00:30:52 -050037public class VibratorService extends IVibratorService.Stub {
38 private static final String TAG = "VibratorService";
Mike Lockwoodcc9a63d2009-11-10 07:50:28 -050039
Patrick Scott18dd5f02009-07-02 11:31:12 -040040 private final LinkedList<Vibration> mVibrations;
41 private Vibration mCurrentVibration;
42
Patrick Scott18dd5f02009-07-02 11:31:12 -040043 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
Mike Lockwood3a322132009-11-24 00:30:52 -050093 VibratorService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 // 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
Patrick Scott18dd5f02009-07-02 11:31:12 -0400104 mVibrations = new LinkedList<Vibration>();
105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 IntentFilter filter = new IntentFilter();
107 filter.addAction(Intent.ACTION_SCREEN_OFF);
108 context.registerReceiver(mIntentReceiver, filter);
109 }
110
Patrick Scott18dd5f02009-07-02 11:31:12 -0400111 public void vibrate(long milliseconds, IBinder token) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700112 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
113 != PackageManager.PERMISSION_GRANTED) {
114 throw new SecurityException("Requires VIBRATE permission");
115 }
Patrick Scott24f10762009-08-19 09:03:56 -0400116 // 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))) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400121 // 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 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 }
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 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800158 Slog.i(TAG, "vibrating with pattern: " + s);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800159 }
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
Patrick Scott18dd5f02009-07-02 11:31:12 -0400168 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);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800187 }
188 }
189 finally {
190 Binder.restoreCallingIdentity(identity);
191 }
192 }
193
Patrick Scott18dd5f02009-07-02 11:31:12 -0400194 public void cancelVibrate(IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 mContext.enforceCallingOrSelfPermission(
196 android.Manifest.permission.VIBRATE,
197 "cancelVibrate");
198
199 // so wakelock calls will succeed
200 long identity = Binder.clearCallingIdentity();
201 try {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400202 synchronized (mVibrations) {
203 final Vibration vib = removeVibrationLocked(token);
204 if (vib == mCurrentVibration) {
205 doCancelVibrateLocked();
206 startNextVibrationLocked();
207 }
208 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 }
210 finally {
211 Binder.restoreCallingIdentity(identity);
212 }
213 }
Eric Olsenf42f15c2009-10-29 16:42:03 -0700214
Patrick Scott18dd5f02009-07-02 11:31:12 -0400215 private final Runnable mVibrationRunnable = new Runnable() {
216 public void run() {
217 synchronized (mVibrations) {
218 doCancelVibrateLocked();
219 startNextVibrationLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400221 }
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();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 }
257 }
258
Patrick Scott18dd5f02009-07-02 11:31:12 -0400259 // 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 private class VibrateThread extends Thread {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400278 final Vibration mVibration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279 boolean mDone;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700280
Patrick Scott18dd5f02009-07-02 11:31:12 -0400281 VibrateThread(Vibration vib) {
282 mVibration = vib;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 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;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400308 long[] pattern = mVibration.mPattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 int len = pattern.length;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400310 int repeat = mVibration.mRepeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 long duration = 0;
312
313 while (!mDone) {
Eric Olsenf42f15c2009-10-29 16:42:03 -0700314 // add off-time duration to any accumulated on-time duration
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 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) {
Mike Lockwood3a322132009-11-24 00:30:52 -0500330 VibratorService.this.vibratorOn(duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 }
332 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400333 if (repeat < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 break;
335 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400336 index = repeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 duration = 0;
338 }
339 }
340 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 mWakeLock.release();
342 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400343 synchronized (mVibrations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 if (mThread == this) {
345 mThread = null;
346 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400347 if (!mDone) {
348 // If this vibration finished naturally, start the next
349 // vibration.
350 mVibrations.remove(mVibration);
351 startNextVibrationLocked();
352 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 }
354 }
355 };
356
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
358 public void onReceive(Context context, Intent intent) {
359 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400360 synchronized (mVibrations) {
361 doCancelVibrateLocked();
362 mVibrations.clear();
363 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
365 }
366 };
Eric Olsenf42f15c2009-10-29 16:42:03 -0700367
Mike Lockwood3a322132009-11-24 00:30:52 -0500368 private Handler mH = new Handler();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700370 private final Context mContext;
371 private final PowerManager.WakeLock mWakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372
373 volatile VibrateThread mThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 native static void vibratorOn(long milliseconds);
376 native static void vibratorOff();
377}