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