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