VibratorService.java revision 5603eca33c8e322997029d3101fd2442df3c274e
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 = (InputManager)mContext.getSystemService(Context.INPUT_SERVICE); 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 int mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE, 458 vib.mUsageHint, vib.mUid, vib.mOpPkg); 459 if (mode == AppOpsManager.MODE_ALLOWED) { 460 mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService), 461 AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg); 462 } 463 if (mode != AppOpsManager.MODE_ALLOWED) { 464 if (mode == AppOpsManager.MODE_ERRORED) { 465 Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid); 466 } 467 mH.post(mVibrationRunnable); 468 return; 469 } 470 } catch (RemoteException e) { 471 } 472 if (vib.mTimeout != 0) { 473 doVibratorOn(vib.mTimeout, vib.mUid, vib.mUsageHint); 474 mH.postDelayed(mVibrationRunnable, vib.mTimeout); 475 } else { 476 // mThread better be null here. doCancelVibrate should always be 477 // called before startNextVibrationLocked or startVibrationLocked. 478 mThread = new VibrateThread(vib); 479 mThread.start(); 480 } 481 } 482 483 private void reportFinishVibrationLocked() { 484 if (mCurrentVibration != null) { 485 try { 486 mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService), 487 AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid, 488 mCurrentVibration.mOpPkg); 489 } catch (RemoteException e) { 490 } 491 mCurrentVibration = null; 492 } 493 } 494 495 // Lock held on mVibrations 496 private Vibration removeVibrationLocked(IBinder token) { 497 ListIterator<Vibration> iter = mVibrations.listIterator(0); 498 while (iter.hasNext()) { 499 Vibration vib = iter.next(); 500 if (vib.mToken == token) { 501 iter.remove(); 502 unlinkVibration(vib); 503 return vib; 504 } 505 } 506 // We might be looking for a simple vibration which is only stored in 507 // mCurrentVibration. 508 if (mCurrentVibration != null && mCurrentVibration.mToken == token) { 509 unlinkVibration(mCurrentVibration); 510 return mCurrentVibration; 511 } 512 return null; 513 } 514 515 private void unlinkVibration(Vibration vib) { 516 if (vib.mPattern != null) { 517 // If Vibration object has a pattern, 518 // the Vibration object has also been linkedToDeath. 519 vib.mToken.unlinkToDeath(vib, 0); 520 } 521 } 522 523 private void updateInputDeviceVibrators() { 524 synchronized (mVibrations) { 525 doCancelVibrateLocked(); 526 527 synchronized (mInputDeviceVibrators) { 528 mVibrateInputDevicesSetting = false; 529 try { 530 mVibrateInputDevicesSetting = Settings.System.getIntForUser( 531 mContext.getContentResolver(), 532 Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0; 533 } catch (SettingNotFoundException snfe) { 534 } 535 536 mLowPowerMode = mPowerManagerInternal.getLowPowerModeEnabled(); 537 538 if (mVibrateInputDevicesSetting) { 539 if (!mInputDeviceListenerRegistered) { 540 mInputDeviceListenerRegistered = true; 541 mIm.registerInputDeviceListener(this, mH); 542 } 543 } else { 544 if (mInputDeviceListenerRegistered) { 545 mInputDeviceListenerRegistered = false; 546 mIm.unregisterInputDeviceListener(this); 547 } 548 } 549 550 mInputDeviceVibrators.clear(); 551 if (mVibrateInputDevicesSetting) { 552 int[] ids = mIm.getInputDeviceIds(); 553 for (int i = 0; i < ids.length; i++) { 554 InputDevice device = mIm.getInputDevice(ids[i]); 555 Vibrator vibrator = device.getVibrator(); 556 if (vibrator.hasVibrator()) { 557 mInputDeviceVibrators.add(vibrator); 558 } 559 } 560 } 561 } 562 563 startNextVibrationLocked(); 564 } 565 } 566 567 @Override 568 public void onInputDeviceAdded(int deviceId) { 569 updateInputDeviceVibrators(); 570 } 571 572 @Override 573 public void onInputDeviceChanged(int deviceId) { 574 updateInputDeviceVibrators(); 575 } 576 577 @Override 578 public void onInputDeviceRemoved(int deviceId) { 579 updateInputDeviceVibrators(); 580 } 581 582 private boolean doVibratorExists() { 583 // For now, we choose to ignore the presence of input devices that have vibrators 584 // when reporting whether the device has a vibrator. Applications often use this 585 // information to decide whether to enable certain features so they expect the 586 // result of hasVibrator() to be constant. For now, just report whether 587 // the device has a built-in vibrator. 588 //synchronized (mInputDeviceVibrators) { 589 // return !mInputDeviceVibrators.isEmpty() || vibratorExists(); 590 //} 591 return vibratorExists(); 592 } 593 594 private void doVibratorOn(long millis, int uid, int usageHint) { 595 synchronized (mInputDeviceVibrators) { 596 if (DEBUG) { 597 Slog.d(TAG, "Turning vibrator on for " + millis + " ms."); 598 } 599 try { 600 mBatteryStatsService.noteVibratorOn(uid, millis); 601 mCurVibUid = uid; 602 } catch (RemoteException e) { 603 } 604 final int vibratorCount = mInputDeviceVibrators.size(); 605 if (vibratorCount != 0) { 606 final AudioAttributes attributes = new AudioAttributes.Builder().setUsage(usageHint) 607 .build(); 608 for (int i = 0; i < vibratorCount; i++) { 609 mInputDeviceVibrators.get(i).vibrate(millis, attributes); 610 } 611 } else { 612 vibratorOn(millis); 613 } 614 } 615 } 616 617 private void doVibratorOff() { 618 synchronized (mInputDeviceVibrators) { 619 if (DEBUG) { 620 Slog.d(TAG, "Turning vibrator off."); 621 } 622 if (mCurVibUid >= 0) { 623 try { 624 mBatteryStatsService.noteVibratorOff(mCurVibUid); 625 } catch (RemoteException e) { 626 } 627 mCurVibUid = -1; 628 } 629 final int vibratorCount = mInputDeviceVibrators.size(); 630 if (vibratorCount != 0) { 631 for (int i = 0; i < vibratorCount; i++) { 632 mInputDeviceVibrators.get(i).cancel(); 633 } 634 } else { 635 vibratorOff(); 636 } 637 } 638 } 639 640 private class VibrateThread extends Thread { 641 final Vibration mVibration; 642 boolean mDone; 643 644 VibrateThread(Vibration vib) { 645 mVibration = vib; 646 mTmpWorkSource.set(vib.mUid); 647 mWakeLock.setWorkSource(mTmpWorkSource); 648 mWakeLock.acquire(); 649 } 650 651 private void delay(long duration) { 652 if (duration > 0) { 653 long bedtime = duration + SystemClock.uptimeMillis(); 654 do { 655 try { 656 this.wait(duration); 657 } 658 catch (InterruptedException e) { 659 } 660 if (mDone) { 661 break; 662 } 663 duration = bedtime - SystemClock.uptimeMillis(); 664 } while (duration > 0); 665 } 666 } 667 668 public void run() { 669 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); 670 synchronized (this) { 671 final long[] pattern = mVibration.mPattern; 672 final int len = pattern.length; 673 final int repeat = mVibration.mRepeat; 674 final int uid = mVibration.mUid; 675 final int usageHint = mVibration.mUsageHint; 676 int index = 0; 677 long duration = 0; 678 679 while (!mDone) { 680 // add off-time duration to any accumulated on-time duration 681 if (index < len) { 682 duration += pattern[index++]; 683 } 684 685 // sleep until it is time to start the vibrator 686 delay(duration); 687 if (mDone) { 688 break; 689 } 690 691 if (index < len) { 692 // read on-time duration and start the vibrator 693 // duration is saved for delay() at top of loop 694 duration = pattern[index++]; 695 if (duration > 0) { 696 VibratorService.this.doVibratorOn(duration, uid, usageHint); 697 } 698 } else { 699 if (repeat < 0) { 700 break; 701 } else { 702 index = repeat; 703 duration = 0; 704 } 705 } 706 } 707 mWakeLock.release(); 708 } 709 synchronized (mVibrations) { 710 if (mThread == this) { 711 mThread = null; 712 } 713 if (!mDone) { 714 // If this vibration finished naturally, start the next 715 // vibration. 716 unlinkVibration(mVibration); 717 startNextVibrationLocked(); 718 } 719 } 720 } 721 } 722 723 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 724 @Override 725 public void onReceive(Context context, Intent intent) { 726 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 727 synchronized (mVibrations) { 728 // When the system is entering a non-interactive state, we want 729 // to cancel vibrations in case a misbehaving app has abandoned 730 // them. However it may happen that the system is currently playing 731 // haptic feedback as part of the transition. So we don't cancel 732 // system vibrations. 733 if (mCurrentVibration != null 734 && !mCurrentVibration.isSystemHapticFeedback()) { 735 doCancelVibrateLocked(); 736 } 737 738 // Clear all remaining vibrations. 739 Iterator<Vibration> it = mVibrations.iterator(); 740 while (it.hasNext()) { 741 Vibration vibration = it.next(); 742 if (vibration != mCurrentVibration) { 743 unlinkVibration(vibration); 744 it.remove(); 745 } 746 } 747 } 748 } 749 } 750 }; 751 752 @Override 753 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 754 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 755 != PackageManager.PERMISSION_GRANTED) { 756 757 pw.println("Permission Denial: can't dump vibrator service from from pid=" 758 + Binder.getCallingPid() 759 + ", uid=" + Binder.getCallingUid()); 760 return; 761 } 762 pw.println("Previous vibrations:"); 763 synchronized (mVibrations) { 764 for (VibrationInfo info : mPreviousVibrations) { 765 pw.print(" "); 766 pw.println(info.toString()); 767 } 768 } 769 } 770} 771