VibratorService.java revision fe9a53bc45fd0124a876dc0a49680aaf86641d3e
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.content.res.Resources; 26import android.database.ContentObserver; 27import android.hardware.input.InputManager; 28import android.hardware.vibrator.V1_0.Constants.EffectStrength; 29import android.media.AudioManager; 30import android.os.PowerSaveState; 31import android.os.BatteryStats; 32import android.os.Handler; 33import android.os.IVibratorService; 34import android.os.PowerManager; 35import android.os.PowerManagerInternal; 36import android.os.Process; 37import android.os.RemoteException; 38import android.os.ResultReceiver; 39import android.os.IBinder; 40import android.os.Binder; 41import android.os.ServiceManager; 42import android.os.ShellCallback; 43import android.os.ShellCommand; 44import android.os.SystemClock; 45import android.os.UserHandle; 46import android.os.Vibrator; 47import android.os.VibrationEffect; 48import android.os.WorkSource; 49import android.provider.Settings; 50import android.provider.Settings.SettingNotFoundException; 51import android.util.Slog; 52import android.view.InputDevice; 53import android.media.AudioAttributes; 54 55import com.android.internal.app.IAppOpsService; 56import com.android.internal.app.IBatteryStats; 57import com.android.internal.util.DumpUtils; 58import com.android.server.power.BatterySaverPolicy.ServiceType; 59 60import java.io.FileDescriptor; 61import java.io.PrintWriter; 62import java.util.ArrayList; 63import java.util.Arrays; 64import java.util.Iterator; 65import java.util.LinkedList; 66import java.util.ListIterator; 67 68public class VibratorService extends IVibratorService.Stub 69 implements InputManager.InputDeviceListener { 70 private static final String TAG = "VibratorService"; 71 private static final boolean DEBUG = false; 72 private static final String SYSTEM_UI_PACKAGE = "com.android.systemui"; 73 74 private final LinkedList<VibrationInfo> mPreviousVibrations; 75 private final int mPreviousVibrationsLimit; 76 private final boolean mSupportsAmplitudeControl; 77 private final int mDefaultVibrationAmplitude; 78 private final VibrationEffect[] mFallbackEffects; 79 private final WorkSource mTmpWorkSource = new WorkSource(); 80 private final Handler mH = new Handler(); 81 private final Object mLock = new Object(); 82 83 private final Context mContext; 84 private final PowerManager.WakeLock mWakeLock; 85 private final IAppOpsService mAppOpsService; 86 private final IBatteryStats mBatteryStatsService; 87 private PowerManagerInternal mPowerManagerInternal; 88 private InputManager mIm; 89 90 private volatile VibrateThread mThread; 91 92 // mInputDeviceVibrators lock should be acquired after mLock, if both are 93 // to be acquired 94 private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>(); 95 private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators 96 private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators 97 98 private Vibration mCurrentVibration; 99 private int mCurVibUid = -1; 100 private boolean mLowPowerMode; 101 private SettingsObserver mSettingObserver; 102 103 native static boolean vibratorExists(); 104 native static void vibratorInit(); 105 native static void vibratorOn(long milliseconds); 106 native static void vibratorOff(); 107 native static boolean vibratorSupportsAmplitudeControl(); 108 native static void vibratorSetAmplitude(int amplitude); 109 native static long vibratorPerformEffect(long effect, long strength); 110 111 private class Vibration implements IBinder.DeathRecipient { 112 private final IBinder mToken; 113 private final VibrationEffect mEffect; 114 private final long mStartTime; 115 private final int mUsageHint; 116 private final int mUid; 117 private final String mOpPkg; 118 119 private Vibration(IBinder token, VibrationEffect effect, 120 int usageHint, int uid, String opPkg) { 121 mToken = token; 122 mEffect = effect; 123 mStartTime = SystemClock.uptimeMillis(); 124 mUsageHint = usageHint; 125 mUid = uid; 126 mOpPkg = opPkg; 127 } 128 129 public void binderDied() { 130 synchronized (mLock) { 131 if (this == mCurrentVibration) { 132 doCancelVibrateLocked(); 133 } 134 } 135 } 136 137 public boolean hasLongerTimeout(long millis) { 138 // If the current effect is a one shot vibration that will end after the given timeout 139 // for the new one shot vibration, then just let the current vibration finish. All 140 // other effect types will get pre-empted. 141 if (mEffect instanceof VibrationEffect.OneShot) { 142 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) mEffect; 143 return mStartTime + oneShot.getTiming() > SystemClock.uptimeMillis() + millis; 144 } 145 return false; 146 } 147 148 public boolean isSystemHapticFeedback() { 149 boolean repeating = false; 150 if (mEffect instanceof VibrationEffect.Waveform) { 151 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) mEffect; 152 repeating = (waveform.getRepeatIndex() < 0); 153 } 154 return (mUid == Process.SYSTEM_UID || mUid == 0 || SYSTEM_UI_PACKAGE.equals(mOpPkg)) 155 && !repeating; 156 } 157 } 158 159 private static class VibrationInfo { 160 private final long mStartTime; 161 private final VibrationEffect mEffect; 162 private final int mUsageHint; 163 private final int mUid; 164 private final String mOpPkg; 165 166 public VibrationInfo(long startTime, VibrationEffect effect, 167 int usageHint, int uid, String opPkg) { 168 mStartTime = startTime; 169 mEffect = effect; 170 mUsageHint = usageHint; 171 mUid = uid; 172 mOpPkg = opPkg; 173 } 174 175 @Override 176 public String toString() { 177 return new StringBuilder() 178 .append(", startTime: ") 179 .append(mStartTime) 180 .append(", effect: ") 181 .append(mEffect) 182 .append(", usageHint: ") 183 .append(mUsageHint) 184 .append(", uid: ") 185 .append(mUid) 186 .append(", opPkg: ") 187 .append(mOpPkg) 188 .toString(); 189 } 190 } 191 192 VibratorService(Context context) { 193 vibratorInit(); 194 // Reset the hardware to a default state, in case this is a runtime 195 // restart instead of a fresh boot. 196 vibratorOff(); 197 198 mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl(); 199 200 mContext = context; 201 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 202 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); 203 mWakeLock.setReferenceCounted(true); 204 205 mAppOpsService = 206 IAppOpsService.Stub.asInterface(ServiceManager.getService(Context.APP_OPS_SERVICE)); 207 mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService( 208 BatteryStats.SERVICE_NAME)); 209 210 mPreviousVibrationsLimit = mContext.getResources().getInteger( 211 com.android.internal.R.integer.config_previousVibrationsDumpLimit); 212 213 mDefaultVibrationAmplitude = mContext.getResources().getInteger( 214 com.android.internal.R.integer.config_defaultVibrationAmplitude); 215 216 mPreviousVibrations = new LinkedList<>(); 217 218 IntentFilter filter = new IntentFilter(); 219 filter.addAction(Intent.ACTION_SCREEN_OFF); 220 context.registerReceiver(mIntentReceiver, filter); 221 222 long[] clickEffectTimings = getLongIntArray(context.getResources(), 223 com.android.internal.R.array.config_virtualKeyVibePattern); 224 VibrationEffect clickEffect = VibrationEffect.createWaveform(clickEffectTimings, -1); 225 VibrationEffect doubleClickEffect = VibrationEffect.createWaveform( 226 new long[] {0, 30, 100, 30} /*timings*/, -1); 227 228 mFallbackEffects = new VibrationEffect[] { clickEffect, doubleClickEffect }; 229 230 } 231 232 public void systemReady() { 233 mIm = mContext.getSystemService(InputManager.class); 234 mSettingObserver = new SettingsObserver(mH); 235 236 mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); 237 mPowerManagerInternal.registerLowPowerModeObserver( 238 new PowerManagerInternal.LowPowerModeListener() { 239 @Override 240 public int getServiceType() { 241 return ServiceType.VIBRATION; 242 } 243 244 @Override 245 public void onLowPowerModeChanged(PowerSaveState result) { 246 updateVibrators(); 247 } 248 }); 249 250 mContext.getContentResolver().registerContentObserver( 251 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), 252 true, mSettingObserver, UserHandle.USER_ALL); 253 254 mContext.registerReceiver(new BroadcastReceiver() { 255 @Override 256 public void onReceive(Context context, Intent intent) { 257 updateVibrators(); 258 } 259 }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH); 260 261 updateVibrators(); 262 } 263 264 private final class SettingsObserver extends ContentObserver { 265 public SettingsObserver(Handler handler) { 266 super(handler); 267 } 268 269 @Override 270 public void onChange(boolean SelfChange) { 271 updateVibrators(); 272 } 273 } 274 275 @Override // Binder call 276 public boolean hasVibrator() { 277 return doVibratorExists(); 278 } 279 280 @Override // Binder call 281 public boolean hasAmplitudeControl() { 282 synchronized (mInputDeviceVibrators) { 283 // Input device vibrators don't support amplitude controls yet, but are still used over 284 // the system vibrator when connected. 285 return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty(); 286 } 287 } 288 289 private void verifyIncomingUid(int uid) { 290 if (uid == Binder.getCallingUid()) { 291 return; 292 } 293 if (Binder.getCallingPid() == Process.myPid()) { 294 return; 295 } 296 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, 297 Binder.getCallingPid(), Binder.getCallingUid(), null); 298 } 299 300 /** 301 * Validate the incoming VibrationEffect. 302 * 303 * We can't throw exceptions here since we might be called from some system_server component, 304 * which would bring the whole system down. 305 * 306 * @return whether the VibrationEffect is valid 307 */ 308 private static boolean verifyVibrationEffect(VibrationEffect effect) { 309 if (effect == null) { 310 // Effect must not be null. 311 Slog.wtf(TAG, "effect must not be null"); 312 return false; 313 } 314 try { 315 effect.validate(); 316 } catch (Exception e) { 317 Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e); 318 return false; 319 } 320 return true; 321 } 322 323 private static long[] getLongIntArray(Resources r, int resid) { 324 int[] ar = r.getIntArray(resid); 325 if (ar == null) { 326 return null; 327 } 328 long[] out = new long[ar.length]; 329 for (int i = 0; i < ar.length; i++) { 330 out[i] = ar[i]; 331 } 332 return out; 333 } 334 335 @Override // Binder call 336 public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, 337 IBinder token) { 338 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) 339 != PackageManager.PERMISSION_GRANTED) { 340 throw new SecurityException("Requires VIBRATE permission"); 341 } 342 if (token == null) { 343 Slog.e(TAG, "token must not be null"); 344 return; 345 } 346 verifyIncomingUid(uid); 347 if (!verifyVibrationEffect(effect)) { 348 return; 349 } 350 351 // If our current vibration is longer than the new vibration and is the same amplitude, 352 // then just let the current one finish. 353 if (effect instanceof VibrationEffect.OneShot 354 && mCurrentVibration != null 355 && mCurrentVibration.mEffect instanceof VibrationEffect.OneShot) { 356 VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect; 357 VibrationEffect.OneShot currentOneShot = 358 (VibrationEffect.OneShot) mCurrentVibration.mEffect; 359 if (mCurrentVibration.hasLongerTimeout(newOneShot.getTiming()) 360 && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) { 361 if (DEBUG) { 362 Slog.e(TAG, "Ignoring incoming vibration in favor of current vibration"); 363 } 364 return; 365 } 366 } 367 368 Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg); 369 370 // Only link against waveforms since they potentially don't have a finish if 371 // they're repeating. Let other effects just play out until they're done. 372 if (effect instanceof VibrationEffect.Waveform) { 373 try { 374 token.linkToDeath(vib, 0); 375 } catch (RemoteException e) { 376 return; 377 } 378 } 379 380 381 long ident = Binder.clearCallingIdentity(); 382 try { 383 synchronized (mLock) { 384 doCancelVibrateLocked(); 385 startVibrationLocked(vib); 386 addToPreviousVibrationsLocked(vib); 387 } 388 } finally { 389 Binder.restoreCallingIdentity(ident); 390 } 391 } 392 393 private void addToPreviousVibrationsLocked(Vibration vib) { 394 if (mPreviousVibrations.size() > mPreviousVibrationsLimit) { 395 mPreviousVibrations.removeFirst(); 396 } 397 mPreviousVibrations.addLast(new VibrationInfo( 398 vib.mStartTime, vib.mEffect, vib.mUsageHint, vib.mUid, vib.mOpPkg)); 399 } 400 401 @Override // Binder call 402 public void cancelVibrate(IBinder token) { 403 mContext.enforceCallingOrSelfPermission( 404 android.Manifest.permission.VIBRATE, 405 "cancelVibrate"); 406 407 synchronized (mLock) { 408 if (mCurrentVibration != null && mCurrentVibration.mToken == token) { 409 if (DEBUG) { 410 Slog.d(TAG, "Canceling vibration."); 411 } 412 long ident = Binder.clearCallingIdentity(); 413 try { 414 doCancelVibrateLocked(); 415 } finally { 416 Binder.restoreCallingIdentity(ident); 417 } 418 } 419 } 420 } 421 422 private final Runnable mVibrationEndRunnable = new Runnable() { 423 @Override 424 public void run() { 425 onVibrationFinished(); 426 } 427 }; 428 429 private void doCancelVibrateLocked() { 430 mH.removeCallbacks(mVibrationEndRunnable); 431 if (mThread != null) { 432 mThread.cancel(); 433 mThread = null; 434 } 435 doVibratorOff(); 436 reportFinishVibrationLocked(); 437 } 438 439 // Callback for whenever the current vibration has finished played out 440 public void onVibrationFinished() { 441 if (DEBUG) { 442 Slog.e(TAG, "Vibration finished, cleaning up"); 443 } 444 synchronized (mLock) { 445 // Make sure the vibration is really done. This also reports that the vibration is 446 // finished. 447 doCancelVibrateLocked(); 448 } 449 } 450 451 private void startVibrationLocked(final Vibration vib) { 452 if (mLowPowerMode && vib.mUsageHint != AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { 453 if (DEBUG) { 454 Slog.e(TAG, "Vibrate ignored, low power mode"); 455 } 456 return; 457 } 458 459 if (vib.mUsageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE && 460 !shouldVibrateForRingtone()) { 461 if (DEBUG) { 462 Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); 463 } 464 return; 465 } 466 467 final int mode = getAppOpMode(vib); 468 if (mode != AppOpsManager.MODE_ALLOWED) { 469 if (mode == AppOpsManager.MODE_ERRORED) { 470 // We might be getting calls from within system_server, so we don't actually want 471 // to throw a SecurityException here. 472 Slog.w(TAG, "Would be an error: vibrate from uid " + vib.mUid); 473 } 474 return; 475 } 476 startVibrationInnerLocked(vib); 477 } 478 479 private void startVibrationInnerLocked(Vibration vib) { 480 mCurrentVibration = vib; 481 if (vib.mEffect instanceof VibrationEffect.OneShot) { 482 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.mEffect; 483 doVibratorOn(oneShot.getTiming(), oneShot.getAmplitude(), vib.mUid, vib.mUsageHint); 484 mH.postDelayed(mVibrationEndRunnable, oneShot.getTiming()); 485 } else if (vib.mEffect instanceof VibrationEffect.Waveform) { 486 // mThread better be null here. doCancelVibrate should always be 487 // called before startNextVibrationLocked or startVibrationLocked. 488 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.mEffect; 489 mThread = new VibrateThread(waveform, vib.mUid, vib.mUsageHint); 490 mThread.start(); 491 } else if (vib.mEffect instanceof VibrationEffect.Prebaked) { 492 long timeout = doVibratorPrebakedEffectLocked(vib); 493 if (timeout > 0) { 494 mH.postDelayed(mVibrationEndRunnable, timeout); 495 } 496 } else { 497 Slog.e(TAG, "Unknown vibration type, ignoring"); 498 } 499 } 500 501 private boolean shouldVibrateForRingtone() { 502 AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 503 int ringerMode = audioManager.getRingerModeInternal(); 504 // "Also vibrate for calls" Setting in Sound 505 if (Settings.System.getInt( 506 mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) { 507 return ringerMode != AudioManager.RINGER_MODE_SILENT; 508 } else { 509 return ringerMode == AudioManager.RINGER_MODE_VIBRATE; 510 } 511 } 512 513 private int getAppOpMode(Vibration vib) { 514 int mode; 515 try { 516 mode = mAppOpsService.checkAudioOperation(AppOpsManager.OP_VIBRATE, 517 vib.mUsageHint, vib.mUid, vib.mOpPkg); 518 if (mode == AppOpsManager.MODE_ALLOWED) { 519 mode = mAppOpsService.startOperation(AppOpsManager.getToken(mAppOpsService), 520 AppOpsManager.OP_VIBRATE, vib.mUid, vib.mOpPkg); 521 } 522 } catch (RemoteException e) { 523 Slog.e(TAG, "Failed to get appop mode for vibration!", e); 524 mode = AppOpsManager.MODE_IGNORED; 525 } 526 return mode; 527 } 528 529 private void reportFinishVibrationLocked() { 530 if (mCurrentVibration != null) { 531 try { 532 mAppOpsService.finishOperation(AppOpsManager.getToken(mAppOpsService), 533 AppOpsManager.OP_VIBRATE, mCurrentVibration.mUid, 534 mCurrentVibration.mOpPkg); 535 } catch (RemoteException e) { } 536 mCurrentVibration = null; 537 } 538 } 539 540 private void unlinkVibration(Vibration vib) { 541 if (vib.mEffect instanceof VibrationEffect.Waveform) { 542 vib.mToken.unlinkToDeath(vib, 0); 543 } 544 } 545 546 private void updateVibrators() { 547 synchronized (mLock) { 548 boolean devicesUpdated = updateInputDeviceVibratorsLocked(); 549 boolean lowPowerModeUpdated = updateLowPowerModeLocked(); 550 551 if (devicesUpdated || lowPowerModeUpdated) { 552 // If the state changes out from under us then just reset. 553 doCancelVibrateLocked(); 554 } 555 } 556 } 557 558 private boolean updateInputDeviceVibratorsLocked() { 559 boolean changed = false; 560 boolean vibrateInputDevices = false; 561 try { 562 vibrateInputDevices = Settings.System.getIntForUser( 563 mContext.getContentResolver(), 564 Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0; 565 } catch (SettingNotFoundException snfe) { 566 } 567 if (vibrateInputDevices != mVibrateInputDevicesSetting) { 568 changed = true; 569 mVibrateInputDevicesSetting = vibrateInputDevices; 570 } 571 572 if (mVibrateInputDevicesSetting) { 573 if (!mInputDeviceListenerRegistered) { 574 mInputDeviceListenerRegistered = true; 575 mIm.registerInputDeviceListener(this, mH); 576 } 577 } else { 578 if (mInputDeviceListenerRegistered) { 579 mInputDeviceListenerRegistered = false; 580 mIm.unregisterInputDeviceListener(this); 581 } 582 } 583 584 mInputDeviceVibrators.clear(); 585 if (mVibrateInputDevicesSetting) { 586 int[] ids = mIm.getInputDeviceIds(); 587 for (int i = 0; i < ids.length; i++) { 588 InputDevice device = mIm.getInputDevice(ids[i]); 589 Vibrator vibrator = device.getVibrator(); 590 if (vibrator.hasVibrator()) { 591 mInputDeviceVibrators.add(vibrator); 592 } 593 } 594 return true; 595 } 596 return changed; 597 } 598 599 private boolean updateLowPowerModeLocked() { 600 boolean lowPowerMode = mPowerManagerInternal 601 .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled; 602 if (lowPowerMode != mLowPowerMode) { 603 mLowPowerMode = lowPowerMode; 604 return true; 605 } 606 return false; 607 } 608 609 @Override 610 public void onInputDeviceAdded(int deviceId) { 611 updateVibrators(); 612 } 613 614 @Override 615 public void onInputDeviceChanged(int deviceId) { 616 updateVibrators(); 617 } 618 619 @Override 620 public void onInputDeviceRemoved(int deviceId) { 621 updateVibrators(); 622 } 623 624 private boolean doVibratorExists() { 625 // For now, we choose to ignore the presence of input devices that have vibrators 626 // when reporting whether the device has a vibrator. Applications often use this 627 // information to decide whether to enable certain features so they expect the 628 // result of hasVibrator() to be constant. For now, just report whether 629 // the device has a built-in vibrator. 630 //synchronized (mInputDeviceVibrators) { 631 // return !mInputDeviceVibrators.isEmpty() || vibratorExists(); 632 //} 633 return vibratorExists(); 634 } 635 636 private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) { 637 synchronized (mInputDeviceVibrators) { 638 if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) { 639 amplitude = mDefaultVibrationAmplitude; 640 } 641 if (DEBUG) { 642 Slog.d(TAG, "Turning vibrator on for " + millis + " ms" + 643 " with amplitude " + amplitude + "."); 644 } 645 noteVibratorOnLocked(uid, millis); 646 final int vibratorCount = mInputDeviceVibrators.size(); 647 if (vibratorCount != 0) { 648 final AudioAttributes attributes = 649 new AudioAttributes.Builder().setUsage(usageHint).build(); 650 for (int i = 0; i < vibratorCount; i++) { 651 mInputDeviceVibrators.get(i).vibrate(millis, attributes); 652 } 653 } else { 654 // Note: ordering is important here! Many haptic drivers will reset their amplitude 655 // when enabled, so we always have to enable frst, then set the amplitude. 656 vibratorOn(millis); 657 doVibratorSetAmplitude(amplitude); 658 } 659 } 660 } 661 662 private void doVibratorSetAmplitude(int amplitude) { 663 if (mSupportsAmplitudeControl) { 664 vibratorSetAmplitude(amplitude); 665 } 666 } 667 668 private void doVibratorOff() { 669 synchronized (mInputDeviceVibrators) { 670 if (DEBUG) { 671 Slog.d(TAG, "Turning vibrator off."); 672 } 673 noteVibratorOffLocked(); 674 final int vibratorCount = mInputDeviceVibrators.size(); 675 if (vibratorCount != 0) { 676 for (int i = 0; i < vibratorCount; i++) { 677 mInputDeviceVibrators.get(i).cancel(); 678 } 679 } else { 680 vibratorOff(); 681 } 682 } 683 } 684 685 private long doVibratorPrebakedEffectLocked(Vibration vib) { 686 synchronized (mInputDeviceVibrators) { 687 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.mEffect; 688 // Input devices don't support prebaked effect, so skip trying it with them. 689 final int vibratorCount = mInputDeviceVibrators.size(); 690 if (vibratorCount == 0) { 691 long timeout = vibratorPerformEffect(prebaked.getId(), EffectStrength.MEDIUM); 692 if (timeout > 0) { 693 noteVibratorOnLocked(vib.mUid, timeout); 694 return timeout; 695 } 696 } 697 final int id = prebaked.getId(); 698 if (id < 0 || id >= mFallbackEffects.length) { 699 Slog.w(TAG, "Failed to play prebaked effect, no fallback"); 700 return 0; 701 } 702 VibrationEffect effect = mFallbackEffects[id]; 703 Vibration fallbackVib = 704 new Vibration(vib.mToken, effect, vib.mUsageHint, vib.mUid, vib.mOpPkg); 705 startVibrationInnerLocked(fallbackVib); 706 } 707 return 0; 708 } 709 710 private void noteVibratorOnLocked(int uid, long millis) { 711 try { 712 mBatteryStatsService.noteVibratorOn(uid, millis); 713 mCurVibUid = uid; 714 } catch (RemoteException e) { 715 } 716 } 717 718 private void noteVibratorOffLocked() { 719 if (mCurVibUid >= 0) { 720 try { 721 mBatteryStatsService.noteVibratorOff(mCurVibUid); 722 } catch (RemoteException e) { } 723 mCurVibUid = -1; 724 } 725 } 726 727 private class VibrateThread extends Thread { 728 private final VibrationEffect.Waveform mWaveform; 729 private final int mUid; 730 private final int mUsageHint; 731 732 private boolean mForceStop; 733 734 VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) { 735 mWaveform = waveform; 736 mUid = uid; 737 mUsageHint = usageHint; 738 mTmpWorkSource.set(uid); 739 mWakeLock.setWorkSource(mTmpWorkSource); 740 } 741 742 private long delayLocked(long duration) { 743 long durationRemaining = duration; 744 if (duration > 0) { 745 final long bedtime = duration + SystemClock.uptimeMillis(); 746 do { 747 try { 748 this.wait(durationRemaining); 749 } 750 catch (InterruptedException e) { } 751 if (mForceStop) { 752 break; 753 } 754 durationRemaining = bedtime - SystemClock.uptimeMillis(); 755 } while (durationRemaining > 0); 756 return duration - durationRemaining; 757 } 758 return 0; 759 } 760 761 public void run() { 762 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); 763 mWakeLock.acquire(); 764 try { 765 boolean finished = playWaveform(); 766 if (finished) { 767 onVibrationFinished(); 768 } 769 } finally { 770 mWakeLock.release(); 771 } 772 } 773 774 /** 775 * Play the waveform. 776 * 777 * @return true if it finished naturally, false otherwise (e.g. it was canceled). 778 */ 779 public boolean playWaveform() { 780 synchronized (this) { 781 final long[] timings = mWaveform.getTimings(); 782 final int[] amplitudes = mWaveform.getAmplitudes(); 783 final int len = timings.length; 784 final int repeat = mWaveform.getRepeatIndex(); 785 786 int index = 0; 787 long onDuration = 0; 788 while (!mForceStop) { 789 if (index < len) { 790 final int amplitude = amplitudes[index]; 791 final long duration = timings[index++]; 792 if (duration <= 0) { 793 continue; 794 } 795 if (amplitude != 0) { 796 if (onDuration <= 0) { 797 // Telling the vibrator to start multiple times usually causes 798 // effects to feel "choppy" because the motor resets at every on 799 // command. Instead we figure out how long our next "on" period is 800 // going to be, tell the motor to stay on for the full duration, 801 // and then wake up to change the amplitude at the appropriate 802 // intervals. 803 onDuration = 804 getTotalOnDuration(timings, amplitudes, index - 1, repeat); 805 doVibratorOn(onDuration, amplitude, mUid, mUsageHint); 806 } else { 807 doVibratorSetAmplitude(amplitude); 808 } 809 } 810 811 long waitTime = delayLocked(duration); 812 if (amplitude != 0) { 813 onDuration -= waitTime; 814 } 815 } else if (repeat < 0) { 816 break; 817 } else { 818 index = repeat; 819 } 820 } 821 return !mForceStop; 822 } 823 } 824 825 public void cancel() { 826 synchronized (this) { 827 mThread.mForceStop = true; 828 mThread.notify(); 829 } 830 } 831 832 /** 833 * Get the duration the vibrator will be on starting at startIndex until the next time it's 834 * off. 835 */ 836 private long getTotalOnDuration( 837 long[] timings, int[] amplitudes, int startIndex, int repeatIndex) { 838 int i = startIndex; 839 long timing = 0; 840 while(amplitudes[i] != 0) { 841 timing += timings[i++]; 842 if (i >= timings.length) { 843 if (repeatIndex >= 0) { 844 i = repeatIndex; 845 } else { 846 break; 847 } 848 } 849 if (i == startIndex) { 850 return 1000; 851 } 852 } 853 return timing; 854 } 855 } 856 857 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 858 @Override 859 public void onReceive(Context context, Intent intent) { 860 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 861 synchronized (mLock) { 862 // When the system is entering a non-interactive state, we want 863 // to cancel vibrations in case a misbehaving app has abandoned 864 // them. However it may happen that the system is currently playing 865 // haptic feedback as part of the transition. So we don't cancel 866 // system vibrations. 867 if (mCurrentVibration != null 868 && !mCurrentVibration.isSystemHapticFeedback()) { 869 doCancelVibrateLocked(); 870 } 871 } 872 } 873 } 874 }; 875 876 @Override 877 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 878 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 879 880 pw.println("Previous vibrations:"); 881 synchronized (mLock) { 882 for (VibrationInfo info : mPreviousVibrations) { 883 pw.print(" "); 884 pw.println(info.toString()); 885 } 886 } 887 } 888 889 @Override 890 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 891 String[] args, ShellCallback callback, ResultReceiver resultReceiver) 892 throws RemoteException { 893 new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver); 894 } 895 896 private final class VibratorShellCommand extends ShellCommand { 897 898 private static final long MAX_VIBRATION_MS = 200; 899 900 private final IBinder mToken; 901 902 private VibratorShellCommand(IBinder token) { 903 mToken = token; 904 } 905 906 @Override 907 public int onCommand(String cmd) { 908 if ("vibrate".equals(cmd)) { 909 return runVibrate(); 910 } 911 return handleDefaultCommands(cmd); 912 } 913 914 private int runVibrate() { 915 final long duration = Long.parseLong(getNextArgRequired()); 916 if (duration > MAX_VIBRATION_MS) { 917 throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS); 918 } 919 String description = getNextArg(); 920 if (description == null) { 921 description = "Shell command"; 922 } 923 924 VibrationEffect effect = 925 VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE); 926 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN, 927 mToken); 928 return 0; 929 } 930 931 @Override 932 public void onHelp() { 933 try (PrintWriter pw = getOutPrintWriter();) { 934 pw.println("Vibrator commands:"); 935 pw.println(" help"); 936 pw.println(" Prints this help text."); 937 pw.println(""); 938 pw.println(" vibrate duration [description]"); 939 pw.println(" Vibrates for duration milliseconds."); 940 pw.println(""); 941 } 942 } 943 } 944 945} 946