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