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