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