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.database.ContentObserver; 25import android.hardware.input.InputManager; 26import android.os.Handler; 27import android.os.IVibratorService; 28import android.os.PowerManager; 29import android.os.Process; 30import android.os.RemoteException; 31import android.os.IBinder; 32import android.os.Binder; 33import android.os.SystemClock; 34import android.os.Vibrator; 35import android.os.WorkSource; 36import android.provider.Settings; 37import android.provider.Settings.SettingNotFoundException; 38import android.util.Slog; 39import android.view.InputDevice; 40 41import java.util.ArrayList; 42import java.util.LinkedList; 43import java.util.ListIterator; 44 45public class VibratorService extends IVibratorService.Stub 46 implements InputManager.InputDeviceListener { 47 private static final String TAG = "VibratorService"; 48 49 private final LinkedList<Vibration> mVibrations; 50 private Vibration mCurrentVibration; 51 private final WorkSource mTmpWorkSource = new WorkSource(); 52 private final Handler mH = new Handler(); 53 54 private final Context mContext; 55 private final PowerManager.WakeLock mWakeLock; 56 private InputManager mIm; 57 58 volatile VibrateThread mThread; 59 60 // mInputDeviceVibrators lock should be acquired after mVibrations lock, if both are 61 // to be acquired 62 private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>(); 63 private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators 64 private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators 65 66 native static boolean vibratorExists(); 67 native static void vibratorOn(long milliseconds); 68 native static void vibratorOff(); 69 70 private class Vibration implements IBinder.DeathRecipient { 71 private final IBinder mToken; 72 private final long mTimeout; 73 private final long mStartTime; 74 private final long[] mPattern; 75 private final int mRepeat; 76 private final int mUid; 77 78 Vibration(IBinder token, long millis, int uid) { 79 this(token, millis, null, 0, uid); 80 } 81 82 Vibration(IBinder token, long[] pattern, int repeat, int uid) { 83 this(token, 0, pattern, repeat, uid); 84 } 85 86 private Vibration(IBinder token, long millis, long[] pattern, 87 int repeat, int uid) { 88 mToken = token; 89 mTimeout = millis; 90 mStartTime = SystemClock.uptimeMillis(); 91 mPattern = pattern; 92 mRepeat = repeat; 93 mUid = uid; 94 } 95 96 public void binderDied() { 97 synchronized (mVibrations) { 98 mVibrations.remove(this); 99 if (this == mCurrentVibration) { 100 doCancelVibrateLocked(); 101 startNextVibrationLocked(); 102 } 103 } 104 } 105 106 public boolean hasLongerTimeout(long millis) { 107 if (mTimeout == 0) { 108 // This is a pattern, return false to play the simple 109 // vibration. 110 return false; 111 } 112 if ((mStartTime + mTimeout) 113 < (SystemClock.uptimeMillis() + millis)) { 114 // If this vibration will end before the time passed in, let 115 // the new vibration play. 116 return false; 117 } 118 return true; 119 } 120 } 121 122 VibratorService(Context context) { 123 // Reset the hardware to a default state, in case this is a runtime 124 // restart instead of a fresh boot. 125 vibratorOff(); 126 127 mContext = context; 128 PowerManager pm = (PowerManager)context.getSystemService( 129 Context.POWER_SERVICE); 130 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); 131 mWakeLock.setReferenceCounted(true); 132 133 mVibrations = new LinkedList<Vibration>(); 134 135 IntentFilter filter = new IntentFilter(); 136 filter.addAction(Intent.ACTION_SCREEN_OFF); 137 context.registerReceiver(mIntentReceiver, filter); 138 } 139 140 public void systemReady() { 141 mIm = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE); 142 mContext.getContentResolver().registerContentObserver( 143 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), true, 144 new ContentObserver(mH) { 145 @Override 146 public void onChange(boolean selfChange) { 147 updateInputDeviceVibrators(); 148 } 149 }); 150 updateInputDeviceVibrators(); 151 } 152 153 public boolean hasVibrator() { 154 return doVibratorExists(); 155 } 156 157 public void vibrate(long milliseconds, IBinder token) { 158 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) 159 != PackageManager.PERMISSION_GRANTED) { 160 throw new SecurityException("Requires VIBRATE permission"); 161 } 162 int uid = Binder.getCallingUid(); 163 // We're running in the system server so we cannot crash. Check for a 164 // timeout of 0 or negative. This will ensure that a vibration has 165 // either a timeout of > 0 or a non-null pattern. 166 if (milliseconds <= 0 || (mCurrentVibration != null 167 && mCurrentVibration.hasLongerTimeout(milliseconds))) { 168 // Ignore this vibration since the current vibration will play for 169 // longer than milliseconds. 170 return; 171 } 172 173 Vibration vib = new Vibration(token, milliseconds, uid); 174 synchronized (mVibrations) { 175 removeVibrationLocked(token); 176 doCancelVibrateLocked(); 177 mCurrentVibration = vib; 178 startVibrationLocked(vib); 179 } 180 } 181 182 private boolean isAll0(long[] pattern) { 183 int N = pattern.length; 184 for (int i = 0; i < N; i++) { 185 if (pattern[i] != 0) { 186 return false; 187 } 188 } 189 return true; 190 } 191 192 public void vibratePattern(long[] pattern, int repeat, IBinder token) { 193 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) 194 != PackageManager.PERMISSION_GRANTED) { 195 throw new SecurityException("Requires VIBRATE permission"); 196 } 197 int uid = Binder.getCallingUid(); 198 // so wakelock calls will succeed 199 long identity = Binder.clearCallingIdentity(); 200 try { 201 if (false) { 202 String s = ""; 203 int N = pattern.length; 204 for (int i=0; i<N; i++) { 205 s += " " + pattern[i]; 206 } 207 Slog.i(TAG, "vibrating with pattern: " + s); 208 } 209 210 // we're running in the server so we can't fail 211 if (pattern == null || pattern.length == 0 212 || isAll0(pattern) 213 || repeat >= pattern.length || token == null) { 214 return; 215 } 216 217 Vibration vib = new Vibration(token, pattern, repeat, uid); 218 try { 219 token.linkToDeath(vib, 0); 220 } catch (RemoteException e) { 221 return; 222 } 223 224 synchronized (mVibrations) { 225 removeVibrationLocked(token); 226 doCancelVibrateLocked(); 227 if (repeat >= 0) { 228 mVibrations.addFirst(vib); 229 startNextVibrationLocked(); 230 } else { 231 // A negative repeat means that this pattern is not meant 232 // to repeat. Treat it like a simple vibration. 233 mCurrentVibration = vib; 234 startVibrationLocked(vib); 235 } 236 } 237 } 238 finally { 239 Binder.restoreCallingIdentity(identity); 240 } 241 } 242 243 public void cancelVibrate(IBinder token) { 244 mContext.enforceCallingOrSelfPermission( 245 android.Manifest.permission.VIBRATE, 246 "cancelVibrate"); 247 248 // so wakelock calls will succeed 249 long identity = Binder.clearCallingIdentity(); 250 try { 251 synchronized (mVibrations) { 252 final Vibration vib = removeVibrationLocked(token); 253 if (vib == mCurrentVibration) { 254 doCancelVibrateLocked(); 255 startNextVibrationLocked(); 256 } 257 } 258 } 259 finally { 260 Binder.restoreCallingIdentity(identity); 261 } 262 } 263 264 private final Runnable mVibrationRunnable = new Runnable() { 265 public void run() { 266 synchronized (mVibrations) { 267 doCancelVibrateLocked(); 268 startNextVibrationLocked(); 269 } 270 } 271 }; 272 273 // Lock held on mVibrations 274 private void doCancelVibrateLocked() { 275 if (mThread != null) { 276 synchronized (mThread) { 277 mThread.mDone = true; 278 mThread.notify(); 279 } 280 mThread = null; 281 } 282 doVibratorOff(); 283 mH.removeCallbacks(mVibrationRunnable); 284 } 285 286 // Lock held on mVibrations 287 private void startNextVibrationLocked() { 288 if (mVibrations.size() <= 0) { 289 mCurrentVibration = null; 290 return; 291 } 292 mCurrentVibration = mVibrations.getFirst(); 293 startVibrationLocked(mCurrentVibration); 294 } 295 296 // Lock held on mVibrations 297 private void startVibrationLocked(final Vibration vib) { 298 if (vib.mTimeout != 0) { 299 doVibratorOn(vib.mTimeout); 300 mH.postDelayed(mVibrationRunnable, vib.mTimeout); 301 } else { 302 // mThread better be null here. doCancelVibrate should always be 303 // called before startNextVibrationLocked or startVibrationLocked. 304 mThread = new VibrateThread(vib); 305 mThread.start(); 306 } 307 } 308 309 // Lock held on mVibrations 310 private Vibration removeVibrationLocked(IBinder token) { 311 ListIterator<Vibration> iter = mVibrations.listIterator(0); 312 while (iter.hasNext()) { 313 Vibration vib = iter.next(); 314 if (vib.mToken == token) { 315 iter.remove(); 316 unlinkVibration(vib); 317 return vib; 318 } 319 } 320 // We might be looking for a simple vibration which is only stored in 321 // mCurrentVibration. 322 if (mCurrentVibration != null && mCurrentVibration.mToken == token) { 323 unlinkVibration(mCurrentVibration); 324 return mCurrentVibration; 325 } 326 return null; 327 } 328 329 private void unlinkVibration(Vibration vib) { 330 if (vib.mPattern != null) { 331 // If Vibration object has a pattern, 332 // the Vibration object has also been linkedToDeath. 333 vib.mToken.unlinkToDeath(vib, 0); 334 } 335 } 336 337 private void updateInputDeviceVibrators() { 338 synchronized (mVibrations) { 339 doCancelVibrateLocked(); 340 341 synchronized (mInputDeviceVibrators) { 342 mVibrateInputDevicesSetting = false; 343 try { 344 mVibrateInputDevicesSetting = Settings.System.getInt(mContext.getContentResolver(), 345 Settings.System.VIBRATE_INPUT_DEVICES) > 0; 346 } catch (SettingNotFoundException snfe) { 347 } 348 349 if (mVibrateInputDevicesSetting) { 350 if (!mInputDeviceListenerRegistered) { 351 mInputDeviceListenerRegistered = true; 352 mIm.registerInputDeviceListener(this, mH); 353 } 354 } else { 355 if (mInputDeviceListenerRegistered) { 356 mInputDeviceListenerRegistered = false; 357 mIm.unregisterInputDeviceListener(this); 358 } 359 } 360 361 mInputDeviceVibrators.clear(); 362 if (mVibrateInputDevicesSetting) { 363 int[] ids = mIm.getInputDeviceIds(); 364 for (int i = 0; i < ids.length; i++) { 365 InputDevice device = mIm.getInputDevice(ids[i]); 366 Vibrator vibrator = device.getVibrator(); 367 if (vibrator.hasVibrator()) { 368 mInputDeviceVibrators.add(vibrator); 369 } 370 } 371 } 372 } 373 374 startNextVibrationLocked(); 375 } 376 } 377 378 @Override 379 public void onInputDeviceAdded(int deviceId) { 380 updateInputDeviceVibrators(); 381 } 382 383 @Override 384 public void onInputDeviceChanged(int deviceId) { 385 updateInputDeviceVibrators(); 386 } 387 388 @Override 389 public void onInputDeviceRemoved(int deviceId) { 390 updateInputDeviceVibrators(); 391 } 392 393 private boolean doVibratorExists() { 394 // For now, we choose to ignore the presence of input devices that have vibrators 395 // when reporting whether the device has a vibrator. Applications often use this 396 // information to decide whether to enable certain features so they expect the 397 // result of hasVibrator() to be constant. For now, just report whether 398 // the device has a built-in vibrator. 399 //synchronized (mInputDeviceVibrators) { 400 // return !mInputDeviceVibrators.isEmpty() || vibratorExists(); 401 //} 402 return vibratorExists(); 403 } 404 405 private void doVibratorOn(long millis) { 406 synchronized (mInputDeviceVibrators) { 407 final int vibratorCount = mInputDeviceVibrators.size(); 408 if (vibratorCount != 0) { 409 for (int i = 0; i < vibratorCount; i++) { 410 mInputDeviceVibrators.get(i).vibrate(millis); 411 } 412 } else { 413 vibratorOn(millis); 414 } 415 } 416 } 417 418 private void doVibratorOff() { 419 synchronized (mInputDeviceVibrators) { 420 final int vibratorCount = mInputDeviceVibrators.size(); 421 if (vibratorCount != 0) { 422 for (int i = 0; i < vibratorCount; i++) { 423 mInputDeviceVibrators.get(i).cancel(); 424 } 425 } else { 426 vibratorOff(); 427 } 428 } 429 } 430 431 private class VibrateThread extends Thread { 432 final Vibration mVibration; 433 boolean mDone; 434 435 VibrateThread(Vibration vib) { 436 mVibration = vib; 437 mTmpWorkSource.set(vib.mUid); 438 mWakeLock.setWorkSource(mTmpWorkSource); 439 mWakeLock.acquire(); 440 } 441 442 private void delay(long duration) { 443 if (duration > 0) { 444 long bedtime = SystemClock.uptimeMillis(); 445 do { 446 try { 447 this.wait(duration); 448 } 449 catch (InterruptedException e) { 450 } 451 if (mDone) { 452 break; 453 } 454 duration = duration 455 - SystemClock.uptimeMillis() - bedtime; 456 } while (duration > 0); 457 } 458 } 459 460 public void run() { 461 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); 462 synchronized (this) { 463 int index = 0; 464 long[] pattern = mVibration.mPattern; 465 int len = pattern.length; 466 int repeat = mVibration.mRepeat; 467 long duration = 0; 468 469 while (!mDone) { 470 // add off-time duration to any accumulated on-time duration 471 if (index < len) { 472 duration += pattern[index++]; 473 } 474 475 // sleep until it is time to start the vibrator 476 delay(duration); 477 if (mDone) { 478 break; 479 } 480 481 if (index < len) { 482 // read on-time duration and start the vibrator 483 // duration is saved for delay() at top of loop 484 duration = pattern[index++]; 485 if (duration > 0) { 486 VibratorService.this.doVibratorOn(duration); 487 } 488 } else { 489 if (repeat < 0) { 490 break; 491 } else { 492 index = repeat; 493 duration = 0; 494 } 495 } 496 } 497 mWakeLock.release(); 498 } 499 synchronized (mVibrations) { 500 if (mThread == this) { 501 mThread = null; 502 } 503 if (!mDone) { 504 // If this vibration finished naturally, start the next 505 // vibration. 506 mVibrations.remove(mVibration); 507 unlinkVibration(mVibration); 508 startNextVibrationLocked(); 509 } 510 } 511 } 512 }; 513 514 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 515 public void onReceive(Context context, Intent intent) { 516 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 517 synchronized (mVibrations) { 518 doCancelVibrateLocked(); 519 520 int size = mVibrations.size(); 521 for(int i = 0; i < size; i++) { 522 unlinkVibration(mVibrations.get(i)); 523 } 524 525 mVibrations.clear(); 526 } 527 } 528 } 529 }; 530} 531