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