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