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