blob: 71353911ea6c56cdceabb2776b563f60bf2431d8 [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;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070032import android.os.WorkSource;
Joe Onorato8a9b2202010-02-26 18:56:32 -080033import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034
Patrick Scott18dd5f02009-07-02 11:31:12 -040035import java.util.LinkedList;
36import java.util.ListIterator;
37
Mike Lockwood3a322132009-11-24 00:30:52 -050038public class VibratorService extends IVibratorService.Stub {
39 private static final String TAG = "VibratorService";
Mike Lockwoodcc9a63d2009-11-10 07:50:28 -050040
Patrick Scott18dd5f02009-07-02 11:31:12 -040041 private final LinkedList<Vibration> mVibrations;
42 private Vibration mCurrentVibration;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070043 private final WorkSource mTmpWorkSource = new WorkSource();
Patrick Scott18dd5f02009-07-02 11:31:12 -040044
Patrick Scott18dd5f02009-07-02 11:31:12 -040045 private class Vibration implements IBinder.DeathRecipient {
46 private final IBinder mToken;
47 private final long mTimeout;
48 private final long mStartTime;
49 private final long[] mPattern;
50 private final int mRepeat;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070051 private final int mUid;
Patrick Scott18dd5f02009-07-02 11:31:12 -040052
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070053 Vibration(IBinder token, long millis, int uid) {
54 this(token, millis, null, 0, uid);
Patrick Scott18dd5f02009-07-02 11:31:12 -040055 }
56
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070057 Vibration(IBinder token, long[] pattern, int repeat, int uid) {
58 this(token, 0, pattern, repeat, uid);
Patrick Scott18dd5f02009-07-02 11:31:12 -040059 }
60
61 private Vibration(IBinder token, long millis, long[] pattern,
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070062 int repeat, int uid) {
Patrick Scott18dd5f02009-07-02 11:31:12 -040063 mToken = token;
64 mTimeout = millis;
65 mStartTime = SystemClock.uptimeMillis();
66 mPattern = pattern;
67 mRepeat = repeat;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -070068 mUid = uid;
Patrick Scott18dd5f02009-07-02 11:31:12 -040069 }
70
71 public void binderDied() {
72 synchronized (mVibrations) {
73 mVibrations.remove(this);
74 if (this == mCurrentVibration) {
75 doCancelVibrateLocked();
76 startNextVibrationLocked();
77 }
78 }
79 }
80
81 public boolean hasLongerTimeout(long millis) {
82 if (mTimeout == 0) {
83 // This is a pattern, return false to play the simple
84 // vibration.
85 return false;
86 }
87 if ((mStartTime + mTimeout)
88 < (SystemClock.uptimeMillis() + millis)) {
89 // If this vibration will end before the time passed in, let
90 // the new vibration play.
91 return false;
92 }
93 return true;
94 }
95 }
96
Mike Lockwood3a322132009-11-24 00:30:52 -050097 VibratorService(Context context) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 // Reset the hardware to a default state, in case this is a runtime
99 // restart instead of a fresh boot.
100 vibratorOff();
101
102 mContext = context;
103 PowerManager pm = (PowerManager)context.getSystemService(
104 Context.POWER_SERVICE);
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700105 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 mWakeLock.setReferenceCounted(true);
107
Patrick Scott18dd5f02009-07-02 11:31:12 -0400108 mVibrations = new LinkedList<Vibration>();
109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 IntentFilter filter = new IntentFilter();
111 filter.addAction(Intent.ACTION_SCREEN_OFF);
112 context.registerReceiver(mIntentReceiver, filter);
113 }
114
Patrick Scott18dd5f02009-07-02 11:31:12 -0400115 public void vibrate(long milliseconds, IBinder token) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700116 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
117 != PackageManager.PERMISSION_GRANTED) {
118 throw new SecurityException("Requires VIBRATE permission");
119 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700120 int uid = Binder.getCallingUid();
Patrick Scott24f10762009-08-19 09:03:56 -0400121 // We're running in the system server so we cannot crash. Check for a
122 // timeout of 0 or negative. This will ensure that a vibration has
123 // either a timeout of > 0 or a non-null pattern.
124 if (milliseconds <= 0 || (mCurrentVibration != null
125 && mCurrentVibration.hasLongerTimeout(milliseconds))) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400126 // Ignore this vibration since the current vibration will play for
127 // longer than milliseconds.
128 return;
129 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700130 Vibration vib = new Vibration(token, milliseconds, uid);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400131 synchronized (mVibrations) {
132 removeVibrationLocked(token);
133 doCancelVibrateLocked();
134 mCurrentVibration = vib;
135 startVibrationLocked(vib);
136 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 }
138
139 private boolean isAll0(long[] pattern) {
140 int N = pattern.length;
141 for (int i = 0; i < N; i++) {
142 if (pattern[i] != 0) {
143 return false;
144 }
145 }
146 return true;
147 }
148
149 public void vibratePattern(long[] pattern, int repeat, IBinder token) {
150 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE)
151 != PackageManager.PERMISSION_GRANTED) {
152 throw new SecurityException("Requires VIBRATE permission");
153 }
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700154 int uid = Binder.getCallingUid();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 // so wakelock calls will succeed
156 long identity = Binder.clearCallingIdentity();
157 try {
158 if (false) {
159 String s = "";
160 int N = pattern.length;
161 for (int i=0; i<N; i++) {
162 s += " " + pattern[i];
163 }
Joe Onorato8a9b2202010-02-26 18:56:32 -0800164 Slog.i(TAG, "vibrating with pattern: " + s);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800165 }
166
167 // we're running in the server so we can't fail
168 if (pattern == null || pattern.length == 0
169 || isAll0(pattern)
170 || repeat >= pattern.length || token == null) {
171 return;
172 }
173
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700174 Vibration vib = new Vibration(token, pattern, repeat, uid);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400175 try {
176 token.linkToDeath(vib, 0);
177 } catch (RemoteException e) {
178 return;
179 }
180
181 synchronized (mVibrations) {
182 removeVibrationLocked(token);
183 doCancelVibrateLocked();
184 if (repeat >= 0) {
185 mVibrations.addFirst(vib);
186 startNextVibrationLocked();
187 } else {
188 // A negative repeat means that this pattern is not meant
189 // to repeat. Treat it like a simple vibration.
190 mCurrentVibration = vib;
191 startVibrationLocked(vib);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 }
194 }
195 finally {
196 Binder.restoreCallingIdentity(identity);
197 }
198 }
199
Patrick Scott18dd5f02009-07-02 11:31:12 -0400200 public void cancelVibrate(IBinder token) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 mContext.enforceCallingOrSelfPermission(
202 android.Manifest.permission.VIBRATE,
203 "cancelVibrate");
204
205 // so wakelock calls will succeed
206 long identity = Binder.clearCallingIdentity();
207 try {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400208 synchronized (mVibrations) {
209 final Vibration vib = removeVibrationLocked(token);
210 if (vib == mCurrentVibration) {
211 doCancelVibrateLocked();
212 startNextVibrationLocked();
213 }
214 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 }
216 finally {
217 Binder.restoreCallingIdentity(identity);
218 }
219 }
Eric Olsenf42f15c2009-10-29 16:42:03 -0700220
Patrick Scott18dd5f02009-07-02 11:31:12 -0400221 private final Runnable mVibrationRunnable = new Runnable() {
222 public void run() {
223 synchronized (mVibrations) {
224 doCancelVibrateLocked();
225 startNextVibrationLocked();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400227 }
228 };
229
230 // Lock held on mVibrations
231 private void doCancelVibrateLocked() {
232 if (mThread != null) {
233 synchronized (mThread) {
234 mThread.mDone = true;
235 mThread.notify();
236 }
237 mThread = null;
238 }
239 vibratorOff();
240 mH.removeCallbacks(mVibrationRunnable);
241 }
242
243 // Lock held on mVibrations
244 private void startNextVibrationLocked() {
245 if (mVibrations.size() <= 0) {
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200246 mCurrentVibration = null;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400247 return;
248 }
249 mCurrentVibration = mVibrations.getFirst();
250 startVibrationLocked(mCurrentVibration);
251 }
252
253 // Lock held on mVibrations
254 private void startVibrationLocked(final Vibration vib) {
255 if (vib.mTimeout != 0) {
256 vibratorOn(vib.mTimeout);
257 mH.postDelayed(mVibrationRunnable, vib.mTimeout);
258 } else {
259 // mThread better be null here. doCancelVibrate should always be
260 // called before startNextVibrationLocked or startVibrationLocked.
261 mThread = new VibrateThread(vib);
262 mThread.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 }
264 }
265
Patrick Scott18dd5f02009-07-02 11:31:12 -0400266 // Lock held on mVibrations
267 private Vibration removeVibrationLocked(IBinder token) {
268 ListIterator<Vibration> iter = mVibrations.listIterator(0);
269 while (iter.hasNext()) {
270 Vibration vib = iter.next();
271 if (vib.mToken == token) {
272 iter.remove();
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200273 unlinkVibration(vib);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400274 return vib;
275 }
276 }
277 // We might be looking for a simple vibration which is only stored in
278 // mCurrentVibration.
279 if (mCurrentVibration != null && mCurrentVibration.mToken == token) {
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200280 unlinkVibration(mCurrentVibration);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400281 return mCurrentVibration;
282 }
283 return null;
284 }
285
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200286 private void unlinkVibration(Vibration vib) {
287 if (vib.mPattern != null) {
288 // If Vibration object has a pattern,
289 // the Vibration object has also been linkedToDeath.
290 vib.mToken.unlinkToDeath(vib, 0);
291 }
292 }
293
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 private class VibrateThread extends Thread {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400295 final Vibration mVibration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 boolean mDone;
Eric Olsenf42f15c2009-10-29 16:42:03 -0700297
Patrick Scott18dd5f02009-07-02 11:31:12 -0400298 VibrateThread(Vibration vib) {
299 mVibration = vib;
Dianne Hackborn7e9f4eb2010-09-10 18:43:00 -0700300 mTmpWorkSource.set(vib.mUid);
301 mWakeLock.setWorkSource(mTmpWorkSource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 mWakeLock.acquire();
303 }
304
305 private void delay(long duration) {
306 if (duration > 0) {
307 long bedtime = SystemClock.uptimeMillis();
308 do {
309 try {
310 this.wait(duration);
311 }
312 catch (InterruptedException e) {
313 }
314 if (mDone) {
315 break;
316 }
317 duration = duration
318 - SystemClock.uptimeMillis() - bedtime;
319 } while (duration > 0);
320 }
321 }
322
323 public void run() {
324 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY);
325 synchronized (this) {
326 int index = 0;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400327 long[] pattern = mVibration.mPattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 int len = pattern.length;
Patrick Scott18dd5f02009-07-02 11:31:12 -0400329 int repeat = mVibration.mRepeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 long duration = 0;
331
332 while (!mDone) {
Eric Olsenf42f15c2009-10-29 16:42:03 -0700333 // add off-time duration to any accumulated on-time duration
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 if (index < len) {
335 duration += pattern[index++];
336 }
337
338 // sleep until it is time to start the vibrator
339 delay(duration);
340 if (mDone) {
341 break;
342 }
343
344 if (index < len) {
345 // read on-time duration and start the vibrator
346 // duration is saved for delay() at top of loop
347 duration = pattern[index++];
348 if (duration > 0) {
Mike Lockwood3a322132009-11-24 00:30:52 -0500349 VibratorService.this.vibratorOn(duration);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 }
351 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400352 if (repeat < 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 break;
354 } else {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400355 index = repeat;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 duration = 0;
357 }
358 }
359 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 mWakeLock.release();
361 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400362 synchronized (mVibrations) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 if (mThread == this) {
364 mThread = null;
365 }
Patrick Scott18dd5f02009-07-02 11:31:12 -0400366 if (!mDone) {
367 // If this vibration finished naturally, start the next
368 // vibration.
369 mVibrations.remove(mVibration);
Mathias Jeppssonb23949b2010-09-28 14:45:23 +0200370 unlinkVibration(mVibration);
Patrick Scott18dd5f02009-07-02 11:31:12 -0400371 startNextVibrationLocked();
372 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 }
374 }
375 };
376
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
378 public void onReceive(Context context, Intent intent) {
379 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
Patrick Scott18dd5f02009-07-02 11:31:12 -0400380 synchronized (mVibrations) {
381 doCancelVibrateLocked();
Vairavan Srinivasan8a61f492011-05-13 10:47:20 -0700382
383 int size = mVibrations.size();
384 for(int i = 0; i < size; i++) {
385 unlinkVibration(mVibrations.get(i));
386 }
387
Patrick Scott18dd5f02009-07-02 11:31:12 -0400388 mVibrations.clear();
389 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390 }
391 }
392 };
Eric Olsenf42f15c2009-10-29 16:42:03 -0700393
Mike Lockwood3a322132009-11-24 00:30:52 -0500394 private Handler mH = new Handler();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395
Dianne Hackbornbed30e12009-03-31 14:46:20 -0700396 private final Context mContext;
397 private final PowerManager.WakeLock mWakeLock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398
399 volatile VibrateThread mThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 native static void vibratorOn(long milliseconds);
402 native static void vibratorOff();
403}