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