1 /* 2 * Copyright (C) 2015 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.telecom; 18 19 import android.app.Notification; 20 import android.app.NotificationManager; 21 import android.app.Person; 22 import android.content.Context; 23 import android.os.VibrationEffect; 24 import android.telecom.Log; 25 import android.telecom.TelecomManager; 26 import android.media.AudioAttributes; 27 import android.media.AudioManager; 28 import android.media.Ringtone; 29 import android.media.VolumeShaper; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.os.Vibrator; 33 34 import com.android.internal.annotations.VisibleForTesting; 35 import com.android.server.telecom.LogUtils.EventTimer; 36 37 import java.util.ArrayList; 38 import java.util.Arrays; 39 import java.util.List; 40 import java.util.concurrent.CompletableFuture; 41 import java.util.concurrent.ExecutionException; 42 import java.util.stream.Collectors; 43 44 /** 45 * Controls the ringtone player. 46 */ 47 @VisibleForTesting 48 public class Ringer { 49 public static class VibrationEffectProxy { createWaveform(long[] timings, int[] amplitudes, int repeat)50 public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) { 51 return VibrationEffect.createWaveform(timings, amplitudes, repeat); 52 } 53 get(Uri ringtoneUri, Context context)54 public VibrationEffect get(Uri ringtoneUri, Context context) { 55 return VibrationEffect.get(ringtoneUri, context); 56 } 57 } 58 @VisibleForTesting 59 public VibrationEffect mDefaultVibrationEffect; 60 61 private static final long[] PULSE_PRIMING_PATTERN = {0,12,250,12,500}; // priming + interval 62 63 private static final int[] PULSE_PRIMING_AMPLITUDE = {0,255,0,255,0}; // priming + interval 64 65 // ease-in + peak + pause 66 private static final long[] PULSE_RAMPING_PATTERN = { 67 50,50,50,50,50,50,50,50,50,50,50,50,50,50,300,1000}; 68 69 // ease-in (min amplitude = 30%) + peak + pause 70 private static final int[] PULSE_RAMPING_AMPLITUDE = { 71 77,77,78,79,81,84,87,93,101,114,133,162,205,255,255,0}; 72 73 private static final long[] PULSE_PATTERN; 74 75 private static final int[] PULSE_AMPLITUDE; 76 77 private static final int RAMPING_RINGER_VIBRATION_DURATION = 5000; 78 private static final int RAMPING_RINGER_DURATION = 10000; 79 80 static { 81 // construct complete pulse pattern 82 PULSE_PATTERN = new long[PULSE_PRIMING_PATTERN.length + PULSE_RAMPING_PATTERN.length]; System.arraycopy( PULSE_PRIMING_PATTERN, 0, PULSE_PATTERN, 0, PULSE_PRIMING_PATTERN.length)83 System.arraycopy( 84 PULSE_PRIMING_PATTERN, 0, PULSE_PATTERN, 0, PULSE_PRIMING_PATTERN.length); System.arraycopy(PULSE_RAMPING_PATTERN, 0, PULSE_PATTERN, PULSE_PRIMING_PATTERN.length, PULSE_RAMPING_PATTERN.length)85 System.arraycopy(PULSE_RAMPING_PATTERN, 0, PULSE_PATTERN, 86 PULSE_PRIMING_PATTERN.length, PULSE_RAMPING_PATTERN.length); 87 88 // construct complete pulse amplitude 89 PULSE_AMPLITUDE = new int[PULSE_PRIMING_AMPLITUDE.length + PULSE_RAMPING_AMPLITUDE.length]; System.arraycopy( PULSE_PRIMING_AMPLITUDE, 0, PULSE_AMPLITUDE, 0, PULSE_PRIMING_AMPLITUDE.length)90 System.arraycopy( 91 PULSE_PRIMING_AMPLITUDE, 0, PULSE_AMPLITUDE, 0, PULSE_PRIMING_AMPLITUDE.length); System.arraycopy(PULSE_RAMPING_AMPLITUDE, 0, PULSE_AMPLITUDE, PULSE_PRIMING_AMPLITUDE.length, PULSE_RAMPING_AMPLITUDE.length)92 System.arraycopy(PULSE_RAMPING_AMPLITUDE, 0, PULSE_AMPLITUDE, 93 PULSE_PRIMING_AMPLITUDE.length, PULSE_RAMPING_AMPLITUDE.length); 94 } 95 96 private static final long[] SIMPLE_VIBRATION_PATTERN = { 97 0, // No delay before starting 98 1000, // How long to vibrate 99 1000, // How long to wait before vibrating again 100 }; 101 102 private static final int[] SIMPLE_VIBRATION_AMPLITUDE = { 103 0, // No delay before starting 104 255, // Vibrate full amplitude 105 0, // No amplitude while waiting 106 }; 107 108 /** 109 * Indicates that vibration should be repeated at element 5 in the {@link #PULSE_AMPLITUDE} and 110 * {@link #PULSE_PATTERN} arrays. This means repetition will happen for the main ease-in/peak 111 * pattern, but the priming + interval part will not be repeated. 112 */ 113 private static final int REPEAT_VIBRATION_AT = 5; 114 115 private static final int REPEAT_SIMPLE_VIBRATION_AT = 1; 116 117 private static final float EPSILON = 1e-6f; 118 119 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder() 120 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) 121 .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) 122 .build(); 123 124 private static VibrationEffect mRampingRingerVibrationEffect; 125 private static VolumeShaper.Configuration mVolumeShaperConfig; 126 127 /** 128 * Used to keep ordering of unanswered incoming calls. There can easily exist multiple incoming 129 * calls and explicit ordering is useful for maintaining the proper state of the ringer. 130 */ 131 132 private final SystemSettingsUtil mSystemSettingsUtil; 133 private final InCallTonePlayer.Factory mPlayerFactory; 134 private final AsyncRingtonePlayer mRingtonePlayer; 135 private final Context mContext; 136 private final Vibrator mVibrator; 137 private final InCallController mInCallController; 138 private final VibrationEffectProxy mVibrationEffectProxy; 139 private final boolean mIsHapticPlaybackSupportedByDevice; 140 /** 141 * For unit testing purposes only; when set, {@link #startRinging(Call, boolean)} will complete 142 * the future provided by the test using {@link #setBlockOnRingingFuture(CompletableFuture)}. 143 */ 144 private CompletableFuture<Void> mBlockOnRingingFuture = null; 145 146 private CompletableFuture<Void> mVibrateFuture = CompletableFuture.completedFuture(null); 147 148 private InCallTonePlayer mCallWaitingPlayer; 149 private RingtoneFactory mRingtoneFactory; 150 151 /** 152 * Call objects that are ringing, vibrating or call-waiting. These are used only for logging 153 * purposes. 154 */ 155 private Call mRingingCall; 156 private Call mVibratingCall; 157 private Call mCallWaitingCall; 158 159 /** 160 * Used to track the status of {@link #mVibrator} in the case of simultaneous incoming calls. 161 */ 162 private boolean mIsVibrating = false; 163 164 /** Initializes the Ringer. */ 165 @VisibleForTesting Ringer( InCallTonePlayer.Factory playerFactory, Context context, SystemSettingsUtil systemSettingsUtil, AsyncRingtonePlayer asyncRingtonePlayer, RingtoneFactory ringtoneFactory, Vibrator vibrator, VibrationEffectProxy vibrationEffectProxy, InCallController inCallController)166 public Ringer( 167 InCallTonePlayer.Factory playerFactory, 168 Context context, 169 SystemSettingsUtil systemSettingsUtil, 170 AsyncRingtonePlayer asyncRingtonePlayer, 171 RingtoneFactory ringtoneFactory, 172 Vibrator vibrator, 173 VibrationEffectProxy vibrationEffectProxy, 174 InCallController inCallController) { 175 176 mSystemSettingsUtil = systemSettingsUtil; 177 mPlayerFactory = playerFactory; 178 mContext = context; 179 // We don't rely on getSystemService(Context.VIBRATOR_SERVICE) to make sure this 180 // vibrator object will be isolated from others. 181 mVibrator = vibrator; 182 mRingtonePlayer = asyncRingtonePlayer; 183 mRingtoneFactory = ringtoneFactory; 184 mInCallController = inCallController; 185 mVibrationEffectProxy = vibrationEffectProxy; 186 187 if (mContext.getResources().getBoolean(R.bool.use_simple_vibration_pattern)) { 188 mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(SIMPLE_VIBRATION_PATTERN, 189 SIMPLE_VIBRATION_AMPLITUDE, REPEAT_SIMPLE_VIBRATION_AT); 190 } else { 191 mDefaultVibrationEffect = mVibrationEffectProxy.createWaveform(PULSE_PATTERN, 192 PULSE_AMPLITUDE, REPEAT_VIBRATION_AT); 193 } 194 195 mIsHapticPlaybackSupportedByDevice = 196 mSystemSettingsUtil.isHapticPlaybackSupported(mContext); 197 } 198 199 @VisibleForTesting setBlockOnRingingFuture(CompletableFuture<Void> future)200 public void setBlockOnRingingFuture(CompletableFuture<Void> future) { 201 mBlockOnRingingFuture = future; 202 } 203 startRinging(Call foregroundCall, boolean isHfpDeviceAttached)204 public boolean startRinging(Call foregroundCall, boolean isHfpDeviceAttached) { 205 if (foregroundCall == null) { 206 Log.wtf(this, "startRinging called with null foreground call."); 207 return false; 208 } 209 210 if (foregroundCall.getState() != CallState.RINGING 211 && foregroundCall.getState() != CallState.SIMULATED_RINGING) { 212 // Its possible for bluetooth to connect JUST as a call goes active, which would mean 213 // the call would start ringing again. 214 Log.i(this, "startRinging called for non-ringing foreground callid=%s", 215 foregroundCall.getId()); 216 return false; 217 } 218 219 AudioManager audioManager = 220 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 221 LogUtils.EventTimer timer = new EventTimer(); 222 boolean isVolumeOverZero = audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0; 223 timer.record("isVolumeOverZero"); 224 boolean shouldRingForContact = shouldRingForContact(foregroundCall.getContactUri()); 225 timer.record("shouldRingForContact"); 226 boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(foregroundCall) == null); 227 timer.record("getRingtone"); 228 boolean isSelfManaged = foregroundCall.isSelfManaged(); 229 timer.record("isSelfManaged"); 230 boolean isSilentRingingRequested = foregroundCall.isSilentRingingRequested(); 231 timer.record("isSilentRingRequested"); 232 233 boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent; 234 timer.record("isRingerAudible"); 235 boolean hasExternalRinger = hasExternalRinger(foregroundCall); 236 timer.record("hasExternalRinger"); 237 // Don't do call waiting operations or vibration unless these are false. 238 boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext); 239 timer.record("isTheaterModeOn"); 240 boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging(); 241 timer.record("letDialerHandleRinging"); 242 243 Log.i(this, "startRinging timings: " + timer); 244 boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged || 245 hasExternalRinger || isSilentRingingRequested; 246 247 // Acquire audio focus under any of the following conditions: 248 // 1. Should ring for contact and there's an HFP device attached 249 // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone 250 // present. 251 // 3. The call is self-managed. 252 boolean shouldAcquireAudioFocus = 253 isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged; 254 255 if (endEarly) { 256 if (letDialerHandleRinging) { 257 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Dialer handles"); 258 } 259 if (isSilentRingingRequested) { 260 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Silent ringing " 261 + "requested"); 262 } 263 Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " + 264 "isSelfManaged=%s, hasExternalRinger=%s, silentRingingRequested=%s", 265 isTheaterModeOn, letDialerHandleRinging, isSelfManaged, hasExternalRinger, 266 isSilentRingingRequested); 267 if (mBlockOnRingingFuture != null) { 268 mBlockOnRingingFuture.complete(null); 269 } 270 return shouldAcquireAudioFocus; 271 } 272 273 stopCallWaiting(); 274 275 VibrationEffect effect; 276 CompletableFuture<Boolean> hapticsFuture = null; 277 // Determine if the settings and DND mode indicate that the vibrator can be used right now. 278 boolean isVibratorEnabled = isVibratorEnabled(mContext, foregroundCall); 279 if (isRingerAudible) { 280 mRingingCall = foregroundCall; 281 Log.addEvent(foregroundCall, LogUtils.Events.START_RINGER); 282 // Because we wait until a contact info query to complete before processing a 283 // call (for the purposes of direct-to-voicemail), the information about custom 284 // ringtones should be available by the time this code executes. We can safely 285 // request the custom ringtone from the call and expect it to be current. 286 if (mSystemSettingsUtil.applyRampingRinger(mContext)) { 287 Log.i(this, "start ramping ringer."); 288 if (mSystemSettingsUtil.enableAudioCoupledVibrationForRampingRinger()) { 289 effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall); 290 } else { 291 effect = mDefaultVibrationEffect; 292 } 293 if (mVolumeShaperConfig == null) { 294 float silencePoint = (float) (RAMPING_RINGER_VIBRATION_DURATION) 295 / (float) (RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION); 296 mVolumeShaperConfig = new VolumeShaper.Configuration.Builder() 297 .setDuration(RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION) 298 .setCurve(new float[] {0.f, silencePoint + EPSILON /*keep monotonicity*/, 299 1.f}, new float[] {0.f, 0.f, 1.f}) 300 .setInterpolatorType(VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR) 301 .build(); 302 } 303 hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, 304 mVolumeShaperConfig, isVibratorEnabled); 305 } else { 306 // Ramping ringtone is not enabled. 307 hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null, 308 isVibratorEnabled); 309 effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall); 310 } 311 } else { 312 String reason = String.format( 313 "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s", 314 isVolumeOverZero, shouldRingForContact, isRingtonePresent); 315 Log.i(this, "startRinging: skipping because ringer would not be audible. " + reason); 316 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Inaudible: " + reason); 317 effect = mDefaultVibrationEffect; 318 } 319 320 if (hapticsFuture != null) { 321 mVibrateFuture = hapticsFuture.thenAccept(isUsingAudioCoupledHaptics -> { 322 if (!isUsingAudioCoupledHaptics || !mIsHapticPlaybackSupportedByDevice) { 323 Log.i(this, "startRinging: fileHasHaptics=%b, hapticsSupported=%b", 324 isUsingAudioCoupledHaptics, mIsHapticPlaybackSupportedByDevice); 325 maybeStartVibration(foregroundCall, shouldRingForContact, effect, 326 isVibratorEnabled, isRingerAudible); 327 } else if (mSystemSettingsUtil.applyRampingRinger(mContext) 328 && !mSystemSettingsUtil.enableAudioCoupledVibrationForRampingRinger()) { 329 Log.i(this, "startRinging: apply ramping ringer vibration"); 330 maybeStartVibration(foregroundCall, shouldRingForContact, effect, 331 isVibratorEnabled, isRingerAudible); 332 } else { 333 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, 334 "using audio-coupled haptics"); 335 } 336 }); 337 if (mBlockOnRingingFuture != null) { 338 mVibrateFuture.whenComplete((v, e) -> mBlockOnRingingFuture.complete(null)); 339 } 340 } else { 341 if (mBlockOnRingingFuture != null) { 342 mBlockOnRingingFuture.complete(null); 343 } 344 Log.w(this, "startRinging: No haptics future; fallback to default behavior"); 345 maybeStartVibration(foregroundCall, shouldRingForContact, effect, isVibratorEnabled, 346 isRingerAudible); 347 } 348 349 return shouldAcquireAudioFocus; 350 } 351 maybeStartVibration(Call foregroundCall, boolean shouldRingForContact, VibrationEffect effect, boolean isVibrationEnabled, boolean isRingerAudible)352 private void maybeStartVibration(Call foregroundCall, boolean shouldRingForContact, 353 VibrationEffect effect, boolean isVibrationEnabled, boolean isRingerAudible) { 354 if (isVibrationEnabled 355 && !mIsVibrating && shouldRingForContact) { 356 if (mSystemSettingsUtil.applyRampingRinger(mContext) 357 && isRingerAudible) { 358 Log.i(this, "start vibration for ramping ringer."); 359 mIsVibrating = true; 360 mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES); 361 } else { 362 Log.i(this, "start normal vibration."); 363 mIsVibrating = true; 364 mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES); 365 } 366 } else if (mIsVibrating) { 367 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating"); 368 } 369 } 370 getVibrationEffectForCall(RingtoneFactory factory, Call call)371 private VibrationEffect getVibrationEffectForCall(RingtoneFactory factory, Call call) { 372 VibrationEffect effect = null; 373 Ringtone ringtone = factory.getRingtone(call); 374 Uri ringtoneUri = ringtone != null ? ringtone.getUri() : null; 375 if (ringtoneUri != null) { 376 try { 377 effect = mVibrationEffectProxy.get(ringtoneUri, mContext); 378 } catch (IllegalArgumentException iae) { 379 // Deep in the bowels of the VibrationEffect class it is possible for an 380 // IllegalArgumentException to be thrown if there is an invalid URI specified in the 381 // device config, or a content provider failure. Rather than crashing the Telecom 382 // process we will just use the default vibration effect. 383 Log.e(this, iae, "getVibrationEffectForCall: failed to get vibration effect"); 384 effect = null; 385 } 386 } 387 388 if (effect == null) { 389 effect = mDefaultVibrationEffect; 390 } 391 return effect; 392 } 393 startCallWaiting(Call call)394 public void startCallWaiting(Call call) { 395 startCallWaiting(call, null); 396 } 397 startCallWaiting(Call call, String reason)398 public void startCallWaiting(Call call, String reason) { 399 if (mSystemSettingsUtil.isTheaterModeOn(mContext)) { 400 return; 401 } 402 403 if (mInCallController.doesConnectedDialerSupportRinging()) { 404 Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Dialer handles"); 405 return; 406 } 407 408 if (call.isSelfManaged()) { 409 Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Self-managed"); 410 return; 411 } 412 413 Log.v(this, "Playing call-waiting tone."); 414 415 stopRinging(); 416 417 if (mCallWaitingPlayer == null) { 418 Log.addEvent(call, LogUtils.Events.START_CALL_WAITING_TONE, reason); 419 mCallWaitingCall = call; 420 mCallWaitingPlayer = 421 mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING); 422 mCallWaitingPlayer.startTone(); 423 } 424 } 425 stopRinging()426 public void stopRinging() { 427 if (mRingingCall != null) { 428 Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER); 429 mRingingCall = null; 430 } 431 432 mRingtonePlayer.stop(); 433 434 // If we haven't started vibrating because we were waiting for the haptics info, cancel 435 // it and don't vibrate at all. 436 if (mVibrateFuture != null) { 437 mVibrateFuture.cancel(true); 438 } 439 440 if (mIsVibrating) { 441 Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR); 442 mVibrator.cancel(); 443 mIsVibrating = false; 444 mVibratingCall = null; 445 } 446 } 447 stopCallWaiting()448 public void stopCallWaiting() { 449 Log.v(this, "stop call waiting."); 450 if (mCallWaitingPlayer != null) { 451 if (mCallWaitingCall != null) { 452 Log.addEvent(mCallWaitingCall, LogUtils.Events.STOP_CALL_WAITING_TONE); 453 mCallWaitingCall = null; 454 } 455 456 mCallWaitingPlayer.stopTone(); 457 mCallWaitingPlayer = null; 458 } 459 } 460 isRinging()461 public boolean isRinging() { 462 return mRingtonePlayer.isPlaying(); 463 } 464 shouldRingForContact(Uri contactUri)465 private boolean shouldRingForContact(Uri contactUri) { 466 final NotificationManager manager = 467 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 468 final Bundle peopleExtras = new Bundle(); 469 if (contactUri != null) { 470 ArrayList<Person> personList = new ArrayList<>(); 471 personList.add(new Person.Builder().setUri(contactUri.toString()).build()); 472 peopleExtras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, personList); 473 } 474 return manager.matchesCallFilter(peopleExtras); 475 } 476 hasExternalRinger(Call foregroundCall)477 private boolean hasExternalRinger(Call foregroundCall) { 478 Bundle intentExtras = foregroundCall.getIntentExtras(); 479 if (intentExtras != null) { 480 return intentExtras.getBoolean(TelecomManager.EXTRA_CALL_EXTERNAL_RINGER, false); 481 } else { 482 return false; 483 } 484 } 485 isVibratorEnabled(Context context, Call call)486 private boolean isVibratorEnabled(Context context, Call call) { 487 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 488 int ringerMode = audioManager.getRingerModeInternal(); 489 boolean shouldVibrate; 490 if (getVibrateWhenRinging(context)) { 491 shouldVibrate = ringerMode != AudioManager.RINGER_MODE_SILENT; 492 } else { 493 shouldVibrate = ringerMode == AudioManager.RINGER_MODE_VIBRATE; 494 } 495 496 // Technically this should be in the calling method, but it seemed a little odd to pass 497 // around a whole bunch of state just for logging purposes. 498 if (shouldVibrate) { 499 Log.addEvent(call, LogUtils.Events.START_VIBRATOR, 500 "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b", 501 mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context), 502 ringerMode, mIsVibrating); 503 } else { 504 Log.addEvent(call, LogUtils.Events.SKIP_VIBRATION, 505 "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b", 506 mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context), 507 ringerMode, mIsVibrating); 508 } 509 510 return shouldVibrate; 511 } 512 getVibrateWhenRinging(Context context)513 private boolean getVibrateWhenRinging(Context context) { 514 if (!mVibrator.hasVibrator()) { 515 return false; 516 } 517 return mSystemSettingsUtil.canVibrateWhenRinging(context) 518 || mSystemSettingsUtil.applyRampingRinger(context); 519 } 520 } 521