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 17 package com.android.server; 18 19 import android.app.AppOpsManager; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.pm.PackageManager; 25 import android.content.res.Resources; 26 import android.database.ContentObserver; 27 import android.hardware.input.InputManager; 28 import android.hardware.vibrator.V1_0.EffectStrength; 29 import android.icu.text.DateFormat; 30 import android.media.AudioManager; 31 import android.os.PowerManager.ServiceType; 32 import android.os.PowerSaveState; 33 import android.os.BatteryStats; 34 import android.os.Handler; 35 import android.os.IVibratorService; 36 import android.os.PowerManager; 37 import android.os.PowerManagerInternal; 38 import android.os.Process; 39 import android.os.RemoteException; 40 import android.os.ResultReceiver; 41 import android.os.IBinder; 42 import android.os.Binder; 43 import android.os.ServiceManager; 44 import android.os.ShellCallback; 45 import android.os.ShellCommand; 46 import android.os.SystemClock; 47 import android.os.Trace; 48 import android.os.UserHandle; 49 import android.os.Vibrator; 50 import android.os.VibrationEffect; 51 import android.os.WorkSource; 52 import android.provider.Settings; 53 import android.provider.Settings.SettingNotFoundException; 54 import android.util.DebugUtils; 55 import android.util.Slog; 56 import android.util.SparseArray; 57 import android.view.InputDevice; 58 import android.media.AudioAttributes; 59 60 import com.android.internal.annotations.GuardedBy; 61 import com.android.internal.app.IAppOpsService; 62 import com.android.internal.app.IBatteryStats; 63 import com.android.internal.util.DumpUtils; 64 65 import java.io.FileDescriptor; 66 import java.io.PrintWriter; 67 import java.util.ArrayList; 68 import java.util.LinkedList; 69 import java.util.Date; 70 71 public class VibratorService extends IVibratorService.Stub 72 implements InputManager.InputDeviceListener { 73 private static final String TAG = "VibratorService"; 74 private static final boolean DEBUG = false; 75 private static final String SYSTEM_UI_PACKAGE = "com.android.systemui"; 76 77 private static final long[] DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS = { 0, 30, 100, 30 }; 78 79 // Scale levels. Each level is defined as the delta between the current setting and the default 80 // intensity for that type of vibration (i.e. current - default). 81 private static final int SCALE_VERY_LOW = -2; 82 private static final int SCALE_LOW = -1; 83 private static final int SCALE_NONE = 0; 84 private static final int SCALE_HIGH = 1; 85 private static final int SCALE_VERY_HIGH = 2; 86 87 // Gamma adjustments for scale levels. 88 private static final float SCALE_VERY_LOW_GAMMA = 2.0f; 89 private static final float SCALE_LOW_GAMMA = 1.5f; 90 private static final float SCALE_NONE_GAMMA = 1.0f; 91 private static final float SCALE_HIGH_GAMMA = 0.5f; 92 private static final float SCALE_VERY_HIGH_GAMMA = 0.25f; 93 94 // Max amplitudes for scale levels. If one is not listed, then the max amplitude is the default 95 // max amplitude. 96 private static final int SCALE_VERY_LOW_MAX_AMPLITUDE = 168; // 2/3 * 255 97 private static final int SCALE_LOW_MAX_AMPLITUDE = 192; // 3/4 * 255 98 99 // If a vibration is playing for longer than 5s, it's probably not haptic feedback. 100 private static final long MAX_HAPTIC_FEEDBACK_DURATION = 5000; 101 102 103 // A mapping from the intensity adjustment to the scaling to apply, where the intensity 104 // adjustment is defined as the delta between the default intensity level and the user selected 105 // intensity level. It's important that we apply the scaling on the delta between the two so 106 // that the default intensity level applies no scaling to application provided effects. 107 private final SparseArray<ScaleLevel> mScaleLevels; 108 private final LinkedList<VibrationInfo> mPreviousVibrations; 109 private final int mPreviousVibrationsLimit; 110 private final boolean mAllowPriorityVibrationsInLowPowerMode; 111 private final boolean mSupportsAmplitudeControl; 112 private final int mDefaultVibrationAmplitude; 113 private final SparseArray<VibrationEffect> mFallbackEffects; 114 private final WorkSource mTmpWorkSource = new WorkSource(); 115 private final Handler mH = new Handler(); 116 private final Object mLock = new Object(); 117 118 private final Context mContext; 119 private final PowerManager.WakeLock mWakeLock; 120 private final AppOpsManager mAppOps; 121 private final IBatteryStats mBatteryStatsService; 122 private PowerManagerInternal mPowerManagerInternal; 123 private InputManager mIm; 124 private Vibrator mVibrator; 125 private SettingsObserver mSettingObserver; 126 127 private volatile VibrateThread mThread; 128 129 // mInputDeviceVibrators lock should be acquired after mLock, if both are 130 // to be acquired 131 private final ArrayList<Vibrator> mInputDeviceVibrators = new ArrayList<Vibrator>(); 132 private boolean mVibrateInputDevicesSetting; // guarded by mInputDeviceVibrators 133 private boolean mInputDeviceListenerRegistered; // guarded by mInputDeviceVibrators 134 135 @GuardedBy("mLock") 136 private Vibration mCurrentVibration; 137 private int mCurVibUid = -1; 138 private boolean mLowPowerMode; 139 private int mHapticFeedbackIntensity; 140 private int mNotificationIntensity; 141 142 native static boolean vibratorExists(); 143 native static void vibratorInit(); 144 native static void vibratorOn(long milliseconds); 145 native static void vibratorOff(); 146 native static boolean vibratorSupportsAmplitudeControl(); 147 native static void vibratorSetAmplitude(int amplitude); 148 native static long vibratorPerformEffect(long effect, long strength); 149 150 private class Vibration implements IBinder.DeathRecipient { 151 public final IBinder token; 152 // Start time in CLOCK_BOOTTIME base. 153 public final long startTime; 154 // Start time in unix epoch time. Only to be used for debugging purposes and to correlate 155 // with other system events, any duration calculations should be done use startTime so as 156 // not to be affected by discontinuities created by RTC adjustments. 157 public final long startTimeDebug; 158 public final int usageHint; 159 public final int uid; 160 public final String opPkg; 161 162 // The actual effect to be played. 163 public VibrationEffect effect; 164 // The original effect that was requested. This is non-null only when the original effect 165 // differs from the effect that's being played. Typically these two things differ because 166 // the effect was scaled based on the users vibration intensity settings. 167 public VibrationEffect originalEffect; 168 169 private Vibration(IBinder token, VibrationEffect effect, 170 int usageHint, int uid, String opPkg) { 171 this.token = token; 172 this.effect = effect; 173 this.startTime = SystemClock.elapsedRealtime(); 174 this.startTimeDebug = System.currentTimeMillis(); 175 this.usageHint = usageHint; 176 this.uid = uid; 177 this.opPkg = opPkg; 178 } 179 180 public void binderDied() { 181 synchronized (mLock) { 182 if (this == mCurrentVibration) { 183 doCancelVibrateLocked(); 184 } 185 } 186 } 187 188 public boolean hasTimeoutLongerThan(long millis) { 189 final long duration = effect.getDuration(); 190 return duration >= 0 && duration > millis; 191 } 192 193 public boolean isHapticFeedback() { 194 if (effect instanceof VibrationEffect.Prebaked) { 195 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) effect; 196 switch (prebaked.getId()) { 197 case VibrationEffect.EFFECT_CLICK: 198 case VibrationEffect.EFFECT_DOUBLE_CLICK: 199 case VibrationEffect.EFFECT_HEAVY_CLICK: 200 case VibrationEffect.EFFECT_TICK: 201 case VibrationEffect.EFFECT_POP: 202 case VibrationEffect.EFFECT_THUD: 203 return true; 204 default: 205 Slog.w(TAG, "Unknown prebaked vibration effect, " 206 + "assuming it isn't haptic feedback."); 207 return false; 208 } 209 } 210 final long duration = effect.getDuration(); 211 return duration >= 0 && duration < MAX_HAPTIC_FEEDBACK_DURATION; 212 } 213 214 public boolean isNotification() { 215 switch (usageHint) { 216 case AudioAttributes.USAGE_NOTIFICATION: 217 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: 218 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: 219 case AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: 220 return true; 221 default: 222 return false; 223 } 224 } 225 226 public boolean isRingtone() { 227 return usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE; 228 } 229 230 public boolean isFromSystem() { 231 return uid == Process.SYSTEM_UID || uid == 0 || SYSTEM_UI_PACKAGE.equals(opPkg); 232 } 233 234 public VibrationInfo toInfo() { 235 return new VibrationInfo( 236 startTimeDebug, effect, originalEffect, usageHint, uid, opPkg); 237 } 238 } 239 240 private static class VibrationInfo { 241 private final long mStartTimeDebug; 242 private final VibrationEffect mEffect; 243 private final VibrationEffect mOriginalEffect; 244 private final int mUsageHint; 245 private final int mUid; 246 private final String mOpPkg; 247 248 public VibrationInfo(long startTimeDebug, VibrationEffect effect, 249 VibrationEffect originalEffect, int usageHint, int uid, String opPkg) { 250 mStartTimeDebug = startTimeDebug; 251 mEffect = effect; 252 mOriginalEffect = originalEffect; 253 mUsageHint = usageHint; 254 mUid = uid; 255 mOpPkg = opPkg; 256 } 257 258 @Override 259 public String toString() { 260 return new StringBuilder() 261 .append("startTime: ") 262 .append(DateFormat.getDateTimeInstance().format(new Date(mStartTimeDebug))) 263 .append(", effect: ") 264 .append(mEffect) 265 .append(", originalEffect: ") 266 .append(mOriginalEffect) 267 .append(", usageHint: ") 268 .append(mUsageHint) 269 .append(", uid: ") 270 .append(mUid) 271 .append(", opPkg: ") 272 .append(mOpPkg) 273 .toString(); 274 } 275 } 276 277 private static final class ScaleLevel { 278 public final float gamma; 279 public final int maxAmplitude; 280 281 public ScaleLevel(float gamma) { 282 this(gamma, VibrationEffect.MAX_AMPLITUDE); 283 } 284 285 public ScaleLevel(float gamma, int maxAmplitude) { 286 this.gamma = gamma; 287 this.maxAmplitude = maxAmplitude; 288 } 289 290 @Override 291 public String toString() { 292 return "ScaleLevel{gamma=" + gamma + ", maxAmplitude=" + maxAmplitude + "}"; 293 } 294 } 295 296 VibratorService(Context context) { 297 vibratorInit(); 298 // Reset the hardware to a default state, in case this is a runtime 299 // restart instead of a fresh boot. 300 vibratorOff(); 301 302 mSupportsAmplitudeControl = vibratorSupportsAmplitudeControl(); 303 304 mContext = context; 305 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 306 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "*vibrator*"); 307 mWakeLock.setReferenceCounted(true); 308 309 mAppOps = mContext.getSystemService(AppOpsManager.class); 310 mBatteryStatsService = IBatteryStats.Stub.asInterface(ServiceManager.getService( 311 BatteryStats.SERVICE_NAME)); 312 313 mPreviousVibrationsLimit = mContext.getResources().getInteger( 314 com.android.internal.R.integer.config_previousVibrationsDumpLimit); 315 316 mDefaultVibrationAmplitude = mContext.getResources().getInteger( 317 com.android.internal.R.integer.config_defaultVibrationAmplitude); 318 319 mAllowPriorityVibrationsInLowPowerMode = mContext.getResources().getBoolean( 320 com.android.internal.R.bool.config_allowPriorityVibrationsInLowPowerMode); 321 322 mPreviousVibrations = new LinkedList<>(); 323 324 IntentFilter filter = new IntentFilter(); 325 filter.addAction(Intent.ACTION_SCREEN_OFF); 326 context.registerReceiver(mIntentReceiver, filter); 327 328 VibrationEffect clickEffect = createEffectFromResource( 329 com.android.internal.R.array.config_virtualKeyVibePattern); 330 VibrationEffect doubleClickEffect = VibrationEffect.createWaveform( 331 DOUBLE_CLICK_EFFECT_FALLBACK_TIMINGS, -1 /*repeatIndex*/); 332 VibrationEffect heavyClickEffect = createEffectFromResource( 333 com.android.internal.R.array.config_longPressVibePattern); 334 VibrationEffect tickEffect = createEffectFromResource( 335 com.android.internal.R.array.config_clockTickVibePattern); 336 337 mFallbackEffects = new SparseArray<>(); 338 mFallbackEffects.put(VibrationEffect.EFFECT_CLICK, clickEffect); 339 mFallbackEffects.put(VibrationEffect.EFFECT_DOUBLE_CLICK, doubleClickEffect); 340 mFallbackEffects.put(VibrationEffect.EFFECT_TICK, tickEffect); 341 mFallbackEffects.put(VibrationEffect.EFFECT_HEAVY_CLICK, heavyClickEffect); 342 343 mScaleLevels = new SparseArray<>(); 344 mScaleLevels.put(SCALE_VERY_LOW, 345 new ScaleLevel(SCALE_VERY_LOW_GAMMA, SCALE_VERY_LOW_MAX_AMPLITUDE)); 346 mScaleLevels.put(SCALE_LOW, new ScaleLevel(SCALE_LOW_GAMMA, SCALE_LOW_MAX_AMPLITUDE)); 347 mScaleLevels.put(SCALE_NONE, new ScaleLevel(SCALE_NONE_GAMMA)); 348 mScaleLevels.put(SCALE_HIGH, new ScaleLevel(SCALE_HIGH_GAMMA)); 349 mScaleLevels.put(SCALE_VERY_HIGH, new ScaleLevel(SCALE_VERY_HIGH_GAMMA)); 350 } 351 352 private VibrationEffect createEffectFromResource(int resId) { 353 long[] timings = getLongIntArray(mContext.getResources(), resId); 354 return createEffectFromTimings(timings); 355 } 356 357 private static VibrationEffect createEffectFromTimings(long[] timings) { 358 if (timings == null || timings.length == 0) { 359 return null; 360 } else if (timings.length == 1) { 361 return VibrationEffect.createOneShot(timings[0], VibrationEffect.DEFAULT_AMPLITUDE); 362 } else { 363 return VibrationEffect.createWaveform(timings, -1); 364 } 365 } 366 367 public void systemReady() { 368 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "VibratorService#systemReady"); 369 try { 370 mIm = mContext.getSystemService(InputManager.class); 371 mVibrator = mContext.getSystemService(Vibrator.class); 372 mSettingObserver = new SettingsObserver(mH); 373 374 mPowerManagerInternal = LocalServices.getService(PowerManagerInternal.class); 375 mPowerManagerInternal.registerLowPowerModeObserver( 376 new PowerManagerInternal.LowPowerModeListener() { 377 @Override 378 public int getServiceType() { 379 return ServiceType.VIBRATION; 380 } 381 382 @Override 383 public void onLowPowerModeChanged(PowerSaveState result) { 384 updateVibrators(); 385 } 386 }); 387 388 mContext.getContentResolver().registerContentObserver( 389 Settings.System.getUriFor(Settings.System.VIBRATE_INPUT_DEVICES), 390 true, mSettingObserver, UserHandle.USER_ALL); 391 392 mContext.getContentResolver().registerContentObserver( 393 Settings.System.getUriFor(Settings.System.HAPTIC_FEEDBACK_INTENSITY), 394 true, mSettingObserver, UserHandle.USER_ALL); 395 396 mContext.getContentResolver().registerContentObserver( 397 Settings.System.getUriFor(Settings.System.NOTIFICATION_VIBRATION_INTENSITY), 398 true, mSettingObserver, UserHandle.USER_ALL); 399 400 mContext.registerReceiver(new BroadcastReceiver() { 401 @Override 402 public void onReceive(Context context, Intent intent) { 403 updateVibrators(); 404 } 405 }, new IntentFilter(Intent.ACTION_USER_SWITCHED), null, mH); 406 407 updateVibrators(); 408 } finally { 409 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 410 } 411 } 412 413 private final class SettingsObserver extends ContentObserver { 414 public SettingsObserver(Handler handler) { 415 super(handler); 416 } 417 418 @Override 419 public void onChange(boolean SelfChange) { 420 updateVibrators(); 421 } 422 } 423 424 @Override // Binder call 425 public boolean hasVibrator() { 426 return doVibratorExists(); 427 } 428 429 @Override // Binder call 430 public boolean hasAmplitudeControl() { 431 synchronized (mInputDeviceVibrators) { 432 // Input device vibrators don't support amplitude controls yet, but are still used over 433 // the system vibrator when connected. 434 return mSupportsAmplitudeControl && mInputDeviceVibrators.isEmpty(); 435 } 436 } 437 438 private void verifyIncomingUid(int uid) { 439 if (uid == Binder.getCallingUid()) { 440 return; 441 } 442 if (Binder.getCallingPid() == Process.myPid()) { 443 return; 444 } 445 mContext.enforcePermission(android.Manifest.permission.UPDATE_APP_OPS_STATS, 446 Binder.getCallingPid(), Binder.getCallingUid(), null); 447 } 448 449 /** 450 * Validate the incoming VibrationEffect. 451 * 452 * We can't throw exceptions here since we might be called from some system_server component, 453 * which would bring the whole system down. 454 * 455 * @return whether the VibrationEffect is valid 456 */ 457 private static boolean verifyVibrationEffect(VibrationEffect effect) { 458 if (effect == null) { 459 // Effect must not be null. 460 Slog.wtf(TAG, "effect must not be null"); 461 return false; 462 } 463 try { 464 effect.validate(); 465 } catch (Exception e) { 466 Slog.wtf(TAG, "Encountered issue when verifying VibrationEffect.", e); 467 return false; 468 } 469 return true; 470 } 471 472 private static long[] getLongIntArray(Resources r, int resid) { 473 int[] ar = r.getIntArray(resid); 474 if (ar == null) { 475 return null; 476 } 477 long[] out = new long[ar.length]; 478 for (int i = 0; i < ar.length; i++) { 479 out[i] = ar[i]; 480 } 481 return out; 482 } 483 484 @Override // Binder call 485 public void vibrate(int uid, String opPkg, VibrationEffect effect, int usageHint, 486 IBinder token) { 487 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "vibrate"); 488 try { 489 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.VIBRATE) 490 != PackageManager.PERMISSION_GRANTED) { 491 throw new SecurityException("Requires VIBRATE permission"); 492 } 493 if (token == null) { 494 Slog.e(TAG, "token must not be null"); 495 return; 496 } 497 verifyIncomingUid(uid); 498 if (!verifyVibrationEffect(effect)) { 499 return; 500 } 501 502 // If our current vibration is longer than the new vibration and is the same amplitude, 503 // then just let the current one finish. 504 synchronized (mLock) { 505 if (effect instanceof VibrationEffect.OneShot 506 && mCurrentVibration != null 507 && mCurrentVibration.effect instanceof VibrationEffect.OneShot) { 508 VibrationEffect.OneShot newOneShot = (VibrationEffect.OneShot) effect; 509 VibrationEffect.OneShot currentOneShot = 510 (VibrationEffect.OneShot) mCurrentVibration.effect; 511 if (mCurrentVibration.hasTimeoutLongerThan(newOneShot.getDuration()) 512 && newOneShot.getAmplitude() == currentOneShot.getAmplitude()) { 513 if (DEBUG) { 514 Slog.d(TAG, 515 "Ignoring incoming vibration in favor of current vibration"); 516 } 517 return; 518 } 519 } 520 521 // If the current vibration is repeating and the incoming one is non-repeating, 522 // then ignore the non-repeating vibration. This is so that we don't cancel 523 // vibrations that are meant to grab the attention of the user, like ringtones and 524 // alarms, in favor of one-shot vibrations that are likely quite short. 525 if (!isRepeatingVibration(effect) 526 && mCurrentVibration != null 527 && isRepeatingVibration(mCurrentVibration.effect)) { 528 if (DEBUG) { 529 Slog.d(TAG, "Ignoring incoming vibration in favor of alarm vibration"); 530 } 531 return; 532 } 533 534 Vibration vib = new Vibration(token, effect, usageHint, uid, opPkg); 535 linkVibration(vib); 536 long ident = Binder.clearCallingIdentity(); 537 try { 538 doCancelVibrateLocked(); 539 startVibrationLocked(vib); 540 addToPreviousVibrationsLocked(vib); 541 } finally { 542 Binder.restoreCallingIdentity(ident); 543 } 544 } 545 } finally { 546 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 547 } 548 } 549 550 private static boolean isRepeatingVibration(VibrationEffect effect) { 551 return effect.getDuration() == Long.MAX_VALUE; 552 } 553 554 private void addToPreviousVibrationsLocked(Vibration vib) { 555 if (mPreviousVibrations.size() > mPreviousVibrationsLimit) { 556 mPreviousVibrations.removeFirst(); 557 } 558 mPreviousVibrations.addLast(vib.toInfo()); 559 } 560 561 @Override // Binder call 562 public void cancelVibrate(IBinder token) { 563 mContext.enforceCallingOrSelfPermission( 564 android.Manifest.permission.VIBRATE, 565 "cancelVibrate"); 566 567 synchronized (mLock) { 568 if (mCurrentVibration != null && mCurrentVibration.token == token) { 569 if (DEBUG) { 570 Slog.d(TAG, "Canceling vibration."); 571 } 572 long ident = Binder.clearCallingIdentity(); 573 try { 574 doCancelVibrateLocked(); 575 } finally { 576 Binder.restoreCallingIdentity(ident); 577 } 578 } 579 } 580 } 581 582 private final Runnable mVibrationEndRunnable = new Runnable() { 583 @Override 584 public void run() { 585 onVibrationFinished(); 586 } 587 }; 588 589 @GuardedBy("mLock") 590 private void doCancelVibrateLocked() { 591 Trace.asyncTraceEnd(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); 592 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doCancelVibrateLocked"); 593 try { 594 mH.removeCallbacks(mVibrationEndRunnable); 595 if (mThread != null) { 596 mThread.cancel(); 597 mThread = null; 598 } 599 doVibratorOff(); 600 reportFinishVibrationLocked(); 601 } finally { 602 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 603 } 604 } 605 606 // Callback for whenever the current vibration has finished played out 607 public void onVibrationFinished() { 608 if (DEBUG) { 609 Slog.e(TAG, "Vibration finished, cleaning up"); 610 } 611 synchronized (mLock) { 612 // Make sure the vibration is really done. This also reports that the vibration is 613 // finished. 614 doCancelVibrateLocked(); 615 } 616 } 617 618 @GuardedBy("mLock") 619 private void startVibrationLocked(final Vibration vib) { 620 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationLocked"); 621 try { 622 if (!isAllowedToVibrateLocked(vib)) { 623 return; 624 } 625 626 final int intensity = getCurrentIntensityLocked(vib); 627 if (intensity == Vibrator.VIBRATION_INTENSITY_OFF) { 628 return; 629 } 630 631 if (vib.isRingtone() && !shouldVibrateForRingtone()) { 632 if (DEBUG) { 633 Slog.e(TAG, "Vibrate ignored, not vibrating for ringtones"); 634 } 635 return; 636 } 637 638 final int mode = getAppOpMode(vib); 639 if (mode != AppOpsManager.MODE_ALLOWED) { 640 if (mode == AppOpsManager.MODE_ERRORED) { 641 // We might be getting calls from within system_server, so we don't actually 642 // want to throw a SecurityException here. 643 Slog.w(TAG, "Would be an error: vibrate from uid " + vib.uid); 644 } 645 return; 646 } 647 applyVibrationIntensityScalingLocked(vib, intensity); 648 startVibrationInnerLocked(vib); 649 } finally { 650 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 651 } 652 } 653 654 @GuardedBy("mLock") 655 private void startVibrationInnerLocked(Vibration vib) { 656 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "startVibrationInnerLocked"); 657 try { 658 mCurrentVibration = vib; 659 if (vib.effect instanceof VibrationEffect.OneShot) { 660 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); 661 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect; 662 doVibratorOn(oneShot.getDuration(), oneShot.getAmplitude(), vib.uid, vib.usageHint); 663 mH.postDelayed(mVibrationEndRunnable, oneShot.getDuration()); 664 } else if (vib.effect instanceof VibrationEffect.Waveform) { 665 // mThread better be null here. doCancelVibrate should always be 666 // called before startNextVibrationLocked or startVibrationLocked. 667 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); 668 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect; 669 mThread = new VibrateThread(waveform, vib.uid, vib.usageHint); 670 mThread.start(); 671 } else if (vib.effect instanceof VibrationEffect.Prebaked) { 672 Trace.asyncTraceBegin(Trace.TRACE_TAG_VIBRATOR, "vibration", 0); 673 long timeout = doVibratorPrebakedEffectLocked(vib); 674 if (timeout > 0) { 675 mH.postDelayed(mVibrationEndRunnable, timeout); 676 } 677 } else { 678 Slog.e(TAG, "Unknown vibration type, ignoring"); 679 } 680 } finally { 681 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 682 } 683 } 684 685 private boolean isAllowedToVibrateLocked(Vibration vib) { 686 if (!mLowPowerMode) { 687 return true; 688 } 689 690 if (vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_RINGTONE) { 691 return true; 692 } 693 694 if (vib.usageHint == AudioAttributes.USAGE_ALARM || 695 vib.usageHint == AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY || 696 vib.usageHint == AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST) { 697 return true; 698 } 699 700 return false; 701 } 702 703 private int getCurrentIntensityLocked(Vibration vib) { 704 if (vib.isNotification() || vib.isRingtone()){ 705 return mNotificationIntensity; 706 } else if (vib.isHapticFeedback()) { 707 return mHapticFeedbackIntensity; 708 } else { 709 return Vibrator.VIBRATION_INTENSITY_MEDIUM; 710 } 711 } 712 713 /** 714 * Scale the vibration effect by the intensity as appropriate based its intent. 715 */ 716 private void applyVibrationIntensityScalingLocked(Vibration vib, int intensity) { 717 if (vib.effect instanceof VibrationEffect.Prebaked) { 718 // Prebaked effects are always just a direct translation from intensity to 719 // EffectStrength. 720 VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked)vib.effect; 721 prebaked.setEffectStrength(intensityToEffectStrength(intensity)); 722 return; 723 } 724 725 final int defaultIntensity; 726 if (vib.isNotification() || vib.isRingtone()) { 727 defaultIntensity = mVibrator.getDefaultNotificationVibrationIntensity(); 728 } else if (vib.isHapticFeedback()) { 729 defaultIntensity = mVibrator.getDefaultHapticFeedbackIntensity(); 730 } else { 731 // If we don't know what kind of vibration we're playing then just skip scaling for 732 // now. 733 return; 734 } 735 736 final ScaleLevel scale = mScaleLevels.get(intensity - defaultIntensity); 737 if (scale == null) { 738 // We should have scaling levels for all cases, so not being able to scale because of a 739 // missing level is unexpected. 740 Slog.e(TAG, "No configured scaling level!" 741 + " (current=" + intensity + ", default= " + defaultIntensity + ")"); 742 return; 743 } 744 745 VibrationEffect scaledEffect = null; 746 if (vib.effect instanceof VibrationEffect.OneShot) { 747 VibrationEffect.OneShot oneShot = (VibrationEffect.OneShot) vib.effect; 748 oneShot = oneShot.resolve(mDefaultVibrationAmplitude); 749 scaledEffect = oneShot.scale(scale.gamma, scale.maxAmplitude); 750 } else if (vib.effect instanceof VibrationEffect.Waveform) { 751 VibrationEffect.Waveform waveform = (VibrationEffect.Waveform) vib.effect; 752 waveform = waveform.resolve(mDefaultVibrationAmplitude); 753 scaledEffect = waveform.scale(scale.gamma, scale.maxAmplitude); 754 } else { 755 Slog.w(TAG, "Unable to apply intensity scaling, unknown VibrationEffect type"); 756 } 757 758 if (scaledEffect != null) { 759 vib.originalEffect = vib.effect; 760 vib.effect = scaledEffect; 761 } 762 } 763 764 private boolean shouldVibrateForRingtone() { 765 AudioManager audioManager = mContext.getSystemService(AudioManager.class); 766 int ringerMode = audioManager.getRingerModeInternal(); 767 // "Also vibrate for calls" Setting in Sound 768 if (Settings.System.getInt( 769 mContext.getContentResolver(), Settings.System.VIBRATE_WHEN_RINGING, 0) != 0) { 770 return ringerMode != AudioManager.RINGER_MODE_SILENT; 771 } else { 772 return ringerMode == AudioManager.RINGER_MODE_VIBRATE; 773 } 774 } 775 776 private int getAppOpMode(Vibration vib) { 777 int mode = mAppOps.checkAudioOpNoThrow(AppOpsManager.OP_VIBRATE, 778 vib.usageHint, vib.uid, vib.opPkg); 779 if (mode == AppOpsManager.MODE_ALLOWED) { 780 mode = mAppOps.startOpNoThrow(AppOpsManager.OP_VIBRATE, vib.uid, vib.opPkg); 781 } 782 return mode; 783 } 784 785 @GuardedBy("mLock") 786 private void reportFinishVibrationLocked() { 787 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "reportFinishVibrationLocked"); 788 try { 789 if (mCurrentVibration != null) { 790 mAppOps.finishOp(AppOpsManager.OP_VIBRATE, mCurrentVibration.uid, 791 mCurrentVibration.opPkg); 792 unlinkVibration(mCurrentVibration); 793 mCurrentVibration = null; 794 } 795 } finally { 796 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 797 } 798 } 799 800 private void linkVibration(Vibration vib) { 801 // Only link against waveforms since they potentially don't have a finish if 802 // they're repeating. Let other effects just play out until they're done. 803 if (vib.effect instanceof VibrationEffect.Waveform) { 804 try { 805 vib.token.linkToDeath(vib, 0); 806 } catch (RemoteException e) { 807 return; 808 } 809 } 810 } 811 812 private void unlinkVibration(Vibration vib) { 813 if (vib.effect instanceof VibrationEffect.Waveform) { 814 vib.token.unlinkToDeath(vib, 0); 815 } 816 } 817 818 private void updateVibrators() { 819 synchronized (mLock) { 820 boolean devicesUpdated = updateInputDeviceVibratorsLocked(); 821 boolean lowPowerModeUpdated = updateLowPowerModeLocked(); 822 updateVibrationIntensityLocked(); 823 824 if (devicesUpdated || lowPowerModeUpdated) { 825 // If the state changes out from under us then just reset. 826 doCancelVibrateLocked(); 827 } 828 } 829 } 830 831 private boolean updateInputDeviceVibratorsLocked() { 832 boolean changed = false; 833 boolean vibrateInputDevices = false; 834 try { 835 vibrateInputDevices = Settings.System.getIntForUser( 836 mContext.getContentResolver(), 837 Settings.System.VIBRATE_INPUT_DEVICES, UserHandle.USER_CURRENT) > 0; 838 } catch (SettingNotFoundException snfe) { 839 } 840 if (vibrateInputDevices != mVibrateInputDevicesSetting) { 841 changed = true; 842 mVibrateInputDevicesSetting = vibrateInputDevices; 843 } 844 845 if (mVibrateInputDevicesSetting) { 846 if (!mInputDeviceListenerRegistered) { 847 mInputDeviceListenerRegistered = true; 848 mIm.registerInputDeviceListener(this, mH); 849 } 850 } else { 851 if (mInputDeviceListenerRegistered) { 852 mInputDeviceListenerRegistered = false; 853 mIm.unregisterInputDeviceListener(this); 854 } 855 } 856 857 mInputDeviceVibrators.clear(); 858 if (mVibrateInputDevicesSetting) { 859 int[] ids = mIm.getInputDeviceIds(); 860 for (int i = 0; i < ids.length; i++) { 861 InputDevice device = mIm.getInputDevice(ids[i]); 862 Vibrator vibrator = device.getVibrator(); 863 if (vibrator.hasVibrator()) { 864 mInputDeviceVibrators.add(vibrator); 865 } 866 } 867 return true; 868 } 869 return changed; 870 } 871 872 private boolean updateLowPowerModeLocked() { 873 boolean lowPowerMode = mPowerManagerInternal 874 .getLowPowerState(ServiceType.VIBRATION).batterySaverEnabled; 875 if (lowPowerMode != mLowPowerMode) { 876 mLowPowerMode = lowPowerMode; 877 return true; 878 } 879 return false; 880 } 881 882 private void updateVibrationIntensityLocked() { 883 mHapticFeedbackIntensity = Settings.System.getIntForUser(mContext.getContentResolver(), 884 Settings.System.HAPTIC_FEEDBACK_INTENSITY, 885 mVibrator.getDefaultHapticFeedbackIntensity(), UserHandle.USER_CURRENT); 886 mNotificationIntensity = Settings.System.getIntForUser(mContext.getContentResolver(), 887 Settings.System.NOTIFICATION_VIBRATION_INTENSITY, 888 mVibrator.getDefaultNotificationVibrationIntensity(), UserHandle.USER_CURRENT); 889 } 890 891 @Override 892 public void onInputDeviceAdded(int deviceId) { 893 updateVibrators(); 894 } 895 896 @Override 897 public void onInputDeviceChanged(int deviceId) { 898 updateVibrators(); 899 } 900 901 @Override 902 public void onInputDeviceRemoved(int deviceId) { 903 updateVibrators(); 904 } 905 906 private boolean doVibratorExists() { 907 // For now, we choose to ignore the presence of input devices that have vibrators 908 // when reporting whether the device has a vibrator. Applications often use this 909 // information to decide whether to enable certain features so they expect the 910 // result of hasVibrator() to be constant. For now, just report whether 911 // the device has a built-in vibrator. 912 //synchronized (mInputDeviceVibrators) { 913 // return !mInputDeviceVibrators.isEmpty() || vibratorExists(); 914 //} 915 return vibratorExists(); 916 } 917 918 private void doVibratorOn(long millis, int amplitude, int uid, int usageHint) { 919 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOn"); 920 try { 921 synchronized (mInputDeviceVibrators) { 922 if (amplitude == VibrationEffect.DEFAULT_AMPLITUDE) { 923 amplitude = mDefaultVibrationAmplitude; 924 } 925 if (DEBUG) { 926 Slog.d(TAG, "Turning vibrator on for " + millis + " ms" + 927 " with amplitude " + amplitude + "."); 928 } 929 noteVibratorOnLocked(uid, millis); 930 final int vibratorCount = mInputDeviceVibrators.size(); 931 if (vibratorCount != 0) { 932 final AudioAttributes attributes = 933 new AudioAttributes.Builder().setUsage(usageHint).build(); 934 for (int i = 0; i < vibratorCount; i++) { 935 mInputDeviceVibrators.get(i).vibrate(millis, attributes); 936 } 937 } else { 938 // Note: ordering is important here! Many haptic drivers will reset their 939 // amplitude when enabled, so we always have to enable frst, then set the 940 // amplitude. 941 vibratorOn(millis); 942 doVibratorSetAmplitude(amplitude); 943 } 944 } 945 } finally { 946 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 947 } 948 } 949 950 private void doVibratorSetAmplitude(int amplitude) { 951 if (mSupportsAmplitudeControl) { 952 vibratorSetAmplitude(amplitude); 953 } 954 } 955 956 private void doVibratorOff() { 957 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorOff"); 958 try { 959 synchronized (mInputDeviceVibrators) { 960 if (DEBUG) { 961 Slog.d(TAG, "Turning vibrator off."); 962 } 963 noteVibratorOffLocked(); 964 final int vibratorCount = mInputDeviceVibrators.size(); 965 if (vibratorCount != 0) { 966 for (int i = 0; i < vibratorCount; i++) { 967 mInputDeviceVibrators.get(i).cancel(); 968 } 969 } else { 970 vibratorOff(); 971 } 972 } 973 } finally { 974 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 975 } 976 } 977 978 @GuardedBy("mLock") 979 private long doVibratorPrebakedEffectLocked(Vibration vib) { 980 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "doVibratorPrebakedEffectLocked"); 981 try { 982 final VibrationEffect.Prebaked prebaked = (VibrationEffect.Prebaked) vib.effect; 983 final boolean usingInputDeviceVibrators; 984 synchronized (mInputDeviceVibrators) { 985 usingInputDeviceVibrators = !mInputDeviceVibrators.isEmpty(); 986 } 987 // Input devices don't support prebaked effect, so skip trying it with them. 988 if (!usingInputDeviceVibrators) { 989 long timeout = vibratorPerformEffect(prebaked.getId(), 990 prebaked.getEffectStrength()); 991 if (timeout > 0) { 992 noteVibratorOnLocked(vib.uid, timeout); 993 return timeout; 994 } 995 } 996 if (!prebaked.shouldFallback()) { 997 return 0; 998 } 999 VibrationEffect effect = getFallbackEffect(prebaked.getId()); 1000 if (effect == null) { 1001 Slog.w(TAG, "Failed to play prebaked effect, no fallback"); 1002 return 0; 1003 } 1004 Vibration fallbackVib = 1005 new Vibration(vib.token, effect, vib.usageHint, vib.uid, vib.opPkg); 1006 final int intensity = getCurrentIntensityLocked(fallbackVib); 1007 linkVibration(fallbackVib); 1008 applyVibrationIntensityScalingLocked(fallbackVib, intensity); 1009 startVibrationInnerLocked(fallbackVib); 1010 return 0; 1011 } finally { 1012 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1013 } 1014 } 1015 1016 private VibrationEffect getFallbackEffect(int effectId) { 1017 return mFallbackEffects.get(effectId); 1018 } 1019 1020 /** 1021 * Return the current desired effect strength. 1022 * 1023 * If the returned value is < 0 then the vibration shouldn't be played at all. 1024 */ 1025 private static int intensityToEffectStrength(int intensity) { 1026 switch (intensity) { 1027 case Vibrator.VIBRATION_INTENSITY_LOW: 1028 return EffectStrength.LIGHT; 1029 case Vibrator.VIBRATION_INTENSITY_MEDIUM: 1030 return EffectStrength.MEDIUM; 1031 case Vibrator.VIBRATION_INTENSITY_HIGH: 1032 return EffectStrength.STRONG; 1033 default: 1034 Slog.w(TAG, "Got unexpected vibration intensity: " + intensity); 1035 return EffectStrength.STRONG; 1036 } 1037 } 1038 1039 private void noteVibratorOnLocked(int uid, long millis) { 1040 try { 1041 mBatteryStatsService.noteVibratorOn(uid, millis); 1042 mCurVibUid = uid; 1043 } catch (RemoteException e) { 1044 } 1045 } 1046 1047 private void noteVibratorOffLocked() { 1048 if (mCurVibUid >= 0) { 1049 try { 1050 mBatteryStatsService.noteVibratorOff(mCurVibUid); 1051 } catch (RemoteException e) { } 1052 mCurVibUid = -1; 1053 } 1054 } 1055 1056 private class VibrateThread extends Thread { 1057 private final VibrationEffect.Waveform mWaveform; 1058 private final int mUid; 1059 private final int mUsageHint; 1060 1061 private boolean mForceStop; 1062 1063 VibrateThread(VibrationEffect.Waveform waveform, int uid, int usageHint) { 1064 mWaveform = waveform; 1065 mUid = uid; 1066 mUsageHint = usageHint; 1067 mTmpWorkSource.set(uid); 1068 mWakeLock.setWorkSource(mTmpWorkSource); 1069 } 1070 1071 private long delayLocked(long duration) { 1072 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "delayLocked"); 1073 try { 1074 long durationRemaining = duration; 1075 if (duration > 0) { 1076 final long bedtime = duration + SystemClock.uptimeMillis(); 1077 do { 1078 try { 1079 this.wait(durationRemaining); 1080 } 1081 catch (InterruptedException e) { } 1082 if (mForceStop) { 1083 break; 1084 } 1085 durationRemaining = bedtime - SystemClock.uptimeMillis(); 1086 } while (durationRemaining > 0); 1087 return duration - durationRemaining; 1088 } 1089 return 0; 1090 } finally { 1091 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1092 } 1093 } 1094 1095 public void run() { 1096 Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_DISPLAY); 1097 mWakeLock.acquire(); 1098 try { 1099 boolean finished = playWaveform(); 1100 if (finished) { 1101 onVibrationFinished(); 1102 } 1103 } finally { 1104 mWakeLock.release(); 1105 } 1106 } 1107 1108 /** 1109 * Play the waveform. 1110 * 1111 * @return true if it finished naturally, false otherwise (e.g. it was canceled). 1112 */ 1113 public boolean playWaveform() { 1114 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "playWaveform"); 1115 try { 1116 synchronized (this) { 1117 final long[] timings = mWaveform.getTimings(); 1118 final int[] amplitudes = mWaveform.getAmplitudes(); 1119 final int len = timings.length; 1120 final int repeat = mWaveform.getRepeatIndex(); 1121 1122 int index = 0; 1123 long onDuration = 0; 1124 while (!mForceStop) { 1125 if (index < len) { 1126 final int amplitude = amplitudes[index]; 1127 final long duration = timings[index++]; 1128 if (duration <= 0) { 1129 continue; 1130 } 1131 if (amplitude != 0) { 1132 if (onDuration <= 0) { 1133 // Telling the vibrator to start multiple times usually causes 1134 // effects to feel "choppy" because the motor resets at every on 1135 // command. Instead we figure out how long our next "on" period 1136 // is going to be, tell the motor to stay on for the full 1137 // duration, and then wake up to change the amplitude at the 1138 // appropriate intervals. 1139 onDuration = getTotalOnDuration(timings, amplitudes, index - 1, 1140 repeat); 1141 doVibratorOn(onDuration, amplitude, mUid, mUsageHint); 1142 } else { 1143 doVibratorSetAmplitude(amplitude); 1144 } 1145 } 1146 1147 long waitTime = delayLocked(duration); 1148 if (amplitude != 0) { 1149 onDuration -= waitTime; 1150 } 1151 } else if (repeat < 0) { 1152 break; 1153 } else { 1154 index = repeat; 1155 } 1156 } 1157 return !mForceStop; 1158 } 1159 } finally { 1160 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1161 } 1162 } 1163 1164 public void cancel() { 1165 synchronized (this) { 1166 mThread.mForceStop = true; 1167 mThread.notify(); 1168 } 1169 } 1170 1171 /** 1172 * Get the duration the vibrator will be on starting at startIndex until the next time it's 1173 * off. 1174 */ 1175 private long getTotalOnDuration( 1176 long[] timings, int[] amplitudes, int startIndex, int repeatIndex) { 1177 int i = startIndex; 1178 long timing = 0; 1179 while(amplitudes[i] != 0) { 1180 timing += timings[i++]; 1181 if (i >= timings.length) { 1182 if (repeatIndex >= 0) { 1183 i = repeatIndex; 1184 } else { 1185 break; 1186 } 1187 } 1188 if (i == startIndex) { 1189 return 1000; 1190 } 1191 } 1192 return timing; 1193 } 1194 } 1195 1196 BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 1197 @Override 1198 public void onReceive(Context context, Intent intent) { 1199 if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { 1200 synchronized (mLock) { 1201 // When the system is entering a non-interactive state, we want 1202 // to cancel vibrations in case a misbehaving app has abandoned 1203 // them. However it may happen that the system is currently playing 1204 // haptic feedback as part of the transition. So we don't cancel 1205 // system vibrations. 1206 if (mCurrentVibration != null 1207 && !(mCurrentVibration.isHapticFeedback() 1208 && mCurrentVibration.isFromSystem())) { 1209 doCancelVibrateLocked(); 1210 } 1211 } 1212 } 1213 } 1214 }; 1215 1216 @Override 1217 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 1218 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) return; 1219 1220 pw.println("Vibrator Service:"); 1221 synchronized (mLock) { 1222 pw.print(" mCurrentVibration="); 1223 if (mCurrentVibration != null) { 1224 pw.println(mCurrentVibration.toInfo().toString()); 1225 } else { 1226 pw.println("null"); 1227 } 1228 pw.println(" mLowPowerMode=" + mLowPowerMode); 1229 pw.println(" mHapticFeedbackIntensity=" + mHapticFeedbackIntensity); 1230 pw.println(" mNotificationIntensity=" + mNotificationIntensity); 1231 pw.println(""); 1232 pw.println(" Previous vibrations:"); 1233 for (VibrationInfo info : mPreviousVibrations) { 1234 pw.print(" "); 1235 pw.println(info.toString()); 1236 } 1237 } 1238 } 1239 1240 @Override 1241 public void onShellCommand(FileDescriptor in, FileDescriptor out, FileDescriptor err, 1242 String[] args, ShellCallback callback, ResultReceiver resultReceiver) 1243 throws RemoteException { 1244 new VibratorShellCommand(this).exec(this, in, out, err, args, callback, resultReceiver); 1245 } 1246 1247 private final class VibratorShellCommand extends ShellCommand { 1248 1249 private static final long MAX_VIBRATION_MS = 200; 1250 1251 private final IBinder mToken; 1252 1253 private VibratorShellCommand(IBinder token) { 1254 mToken = token; 1255 } 1256 1257 @Override 1258 public int onCommand(String cmd) { 1259 if ("vibrate".equals(cmd)) { 1260 return runVibrate(); 1261 } 1262 return handleDefaultCommands(cmd); 1263 } 1264 1265 private int runVibrate() { 1266 Trace.traceBegin(Trace.TRACE_TAG_VIBRATOR, "runVibrate"); 1267 try { 1268 try { 1269 final int zenMode = Settings.Global.getInt(mContext.getContentResolver(), 1270 Settings.Global.ZEN_MODE); 1271 if (zenMode != Settings.Global.ZEN_MODE_OFF) { 1272 try (PrintWriter pw = getOutPrintWriter();) { 1273 pw.print("Ignoring because device is on DND mode "); 1274 pw.println(DebugUtils.flagsToString(Settings.Global.class, "ZEN_MODE_", 1275 zenMode)); 1276 return 0; 1277 } 1278 } 1279 } catch (SettingNotFoundException e) { 1280 // ignore 1281 } 1282 1283 final long duration = Long.parseLong(getNextArgRequired()); 1284 if (duration > MAX_VIBRATION_MS) { 1285 throw new IllegalArgumentException("maximum duration is " + MAX_VIBRATION_MS); 1286 } 1287 String description = getNextArg(); 1288 if (description == null) { 1289 description = "Shell command"; 1290 } 1291 1292 VibrationEffect effect = 1293 VibrationEffect.createOneShot(duration, VibrationEffect.DEFAULT_AMPLITUDE); 1294 vibrate(Binder.getCallingUid(), description, effect, AudioAttributes.USAGE_UNKNOWN, 1295 mToken); 1296 return 0; 1297 } finally { 1298 Trace.traceEnd(Trace.TRACE_TAG_VIBRATOR); 1299 } 1300 } 1301 1302 @Override 1303 public void onHelp() { 1304 try (PrintWriter pw = getOutPrintWriter();) { 1305 pw.println("Vibrator commands:"); 1306 pw.println(" help"); 1307 pw.println(" Prints this help text."); 1308 pw.println(""); 1309 pw.println(" vibrate duration [description]"); 1310 pw.println(" Vibrates for duration milliseconds; ignored when device is on DND "); 1311 pw.println(" (Do Not Disturb) mode."); 1312 pw.println(""); 1313 } 1314 } 1315 } 1316 1317 } 1318