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 { 51 public VibrationEffect createWaveform(long[] timings, int[] amplitudes, int repeat) { 52 return VibrationEffect.createWaveform(timings, amplitudes, repeat); 53 } 54 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]; 84 System.arraycopy( 85 PULSE_PRIMING_PATTERN, 0, PULSE_PATTERN, 0, PULSE_PRIMING_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]; 91 System.arraycopy( 92 PULSE_PRIMING_AMPLITUDE, 0, PULSE_AMPLITUDE, 0, PULSE_PRIMING_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 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 207 public void setBlockOnRingingFuture(CompletableFuture<Void> future) { 208 mBlockOnRingingFuture = future; 209 } 210 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( 286 RAMPING_RINGER_VIBRATION_DURATION + RAMPING_RINGER_DURATION) 287 .setCurve(new float[]{0.f, silencePoint + EPSILON /*keep monotonicity*/, 288 1.f}, new float[]{0.f, 0.f, 1.f}) 289 .setInterpolatorType( 290 VolumeShaper.Configuration.INTERPOLATOR_TYPE_LINEAR) 291 .build(); 292 } 293 hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, 294 mVolumeShaperConfig, attributes.isRingerAudible(), isVibratorEnabled); 295 } else { 296 // Ramping ringtone is not enabled. 297 hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null, 298 attributes.isRingerAudible(), isVibratorEnabled); 299 effect = getVibrationEffectForCall(mRingtoneFactory, foregroundCall); 300 } 301 } else { 302 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_RINGING, "Inaudible: " 303 + attributes.getInaudibleReason()); 304 if (isVibratorEnabled && mIsHapticPlaybackSupportedByDevice) { 305 // Attempt to run the attentional haptic ringtone first and fallback to the default 306 // vibration effect if hapticFuture is completed with false. 307 hapticsFuture = mRingtonePlayer.play(mRingtoneFactory, foregroundCall, null, 308 attributes.isRingerAudible(), isVibratorEnabled); 309 } 310 effect = mDefaultVibrationEffect; 311 } 312 313 if (hapticsFuture != null) { 314 final boolean shouldRingForContact = attributes.shouldRingForContact(); 315 final boolean isRingerAudible = attributes.isRingerAudible(); 316 mVibrateFuture = hapticsFuture.thenAccept(isUsingAudioCoupledHaptics -> { 317 if (!isUsingAudioCoupledHaptics || !mIsHapticPlaybackSupportedByDevice) { 318 Log.i(this, "startRinging: fileHasHaptics=%b, hapticsSupported=%b", 319 isUsingAudioCoupledHaptics, mIsHapticPlaybackSupportedByDevice); 320 maybeStartVibration(foregroundCall, shouldRingForContact, effect, 321 isVibratorEnabled, isRingerAudible); 322 } else if (mSystemSettingsUtil.applyRampingRinger(mContext) 323 && !mSystemSettingsUtil.enableAudioCoupledVibrationForRampingRinger()) { 324 Log.i(this, "startRinging: apply ramping ringer vibration"); 325 maybeStartVibration(foregroundCall, shouldRingForContact, effect, 326 isVibratorEnabled, isRingerAudible); 327 } else { 328 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, 329 "using audio-coupled haptics"); 330 } 331 }); 332 if (mBlockOnRingingFuture != null) { 333 mVibrateFuture.whenComplete((v, e) -> mBlockOnRingingFuture.complete(null)); 334 } 335 } else { 336 if (mBlockOnRingingFuture != null) { 337 mBlockOnRingingFuture.complete(null); 338 } 339 Log.w(this, "startRinging: No haptics future; fallback to default behavior"); 340 maybeStartVibration(foregroundCall, attributes.shouldRingForContact(), effect, 341 isVibratorEnabled, attributes.isRingerAudible()); 342 } 343 344 return attributes.shouldAcquireAudioFocus(); 345 } 346 347 private void maybeStartVibration(Call foregroundCall, boolean shouldRingForContact, 348 VibrationEffect effect, boolean isVibrationEnabled, boolean isRingerAudible) { 349 if (isVibrationEnabled 350 && !mIsVibrating && shouldRingForContact) { 351 if (mSystemSettingsUtil.applyRampingRinger(mContext) 352 && isRingerAudible) { 353 Log.i(this, "start vibration for ramping ringer."); 354 mIsVibrating = true; 355 mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES); 356 } else { 357 Log.i(this, "start normal vibration."); 358 mIsVibrating = true; 359 mVibrator.vibrate(effect, VIBRATION_ATTRIBUTES); 360 } 361 } else if (mIsVibrating) { 362 Log.addEvent(foregroundCall, LogUtils.Events.SKIP_VIBRATION, "already vibrating"); 363 } 364 } 365 366 private VibrationEffect getVibrationEffectForCall(RingtoneFactory factory, Call call) { 367 VibrationEffect effect = null; 368 Ringtone ringtone = factory.getRingtone(call); 369 Uri ringtoneUri = ringtone != null ? ringtone.getUri() : null; 370 if (ringtoneUri != null) { 371 try { 372 effect = mVibrationEffectProxy.get(ringtoneUri, mContext); 373 } catch (IllegalArgumentException iae) { 374 // Deep in the bowels of the VibrationEffect class it is possible for an 375 // IllegalArgumentException to be thrown if there is an invalid URI specified in the 376 // device config, or a content provider failure. Rather than crashing the Telecom 377 // process we will just use the default vibration effect. 378 Log.e(this, iae, "getVibrationEffectForCall: failed to get vibration effect"); 379 effect = null; 380 } 381 } 382 383 if (effect == null) { 384 effect = mDefaultVibrationEffect; 385 } 386 return effect; 387 } 388 389 public void startCallWaiting(Call call) { 390 startCallWaiting(call, null); 391 } 392 393 public void startCallWaiting(Call call, String reason) { 394 if (mSystemSettingsUtil.isTheaterModeOn(mContext)) { 395 return; 396 } 397 398 if (mInCallController.doesConnectedDialerSupportRinging()) { 399 Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Dialer handles"); 400 return; 401 } 402 403 if (call.isSelfManaged()) { 404 Log.addEvent(call, LogUtils.Events.SKIP_RINGING, "Self-managed"); 405 return; 406 } 407 408 Log.v(this, "Playing call-waiting tone."); 409 410 stopRinging(); 411 412 if (mCallWaitingPlayer == null) { 413 Log.addEvent(call, LogUtils.Events.START_CALL_WAITING_TONE, reason); 414 mCallWaitingCall = call; 415 mCallWaitingPlayer = 416 mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING); 417 mCallWaitingPlayer.startTone(); 418 } 419 } 420 421 public void stopRinging() { 422 if (mRingingCall != null) { 423 Log.addEvent(mRingingCall, LogUtils.Events.STOP_RINGER); 424 mRingingCall = null; 425 } 426 427 mRingtonePlayer.stop(); 428 429 // If we haven't started vibrating because we were waiting for the haptics info, cancel 430 // it and don't vibrate at all. 431 if (mVibrateFuture != null) { 432 mVibrateFuture.cancel(true); 433 } 434 435 if (mIsVibrating) { 436 Log.addEvent(mVibratingCall, LogUtils.Events.STOP_VIBRATOR); 437 mVibrator.cancel(); 438 mIsVibrating = false; 439 mVibratingCall = null; 440 } 441 } 442 443 public void stopCallWaiting() { 444 Log.v(this, "stop call waiting."); 445 if (mCallWaitingPlayer != null) { 446 if (mCallWaitingCall != null) { 447 Log.addEvent(mCallWaitingCall, LogUtils.Events.STOP_CALL_WAITING_TONE); 448 mCallWaitingCall = null; 449 } 450 451 mCallWaitingPlayer.stopTone(); 452 mCallWaitingPlayer = null; 453 } 454 } 455 456 public boolean isRinging() { 457 return mRingtonePlayer.isPlaying(); 458 } 459 460 private boolean shouldRingForContact(Uri contactUri) { 461 final NotificationManager manager = 462 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); 463 final Bundle peopleExtras = new Bundle(); 464 if (contactUri != null) { 465 ArrayList<Person> personList = new ArrayList<>(); 466 personList.add(new Person.Builder().setUri(contactUri.toString()).build()); 467 peopleExtras.putParcelableArrayList(Notification.EXTRA_PEOPLE_LIST, personList); 468 } 469 return manager.matchesCallFilter(peopleExtras); 470 } 471 472 private boolean hasExternalRinger(Call foregroundCall) { 473 Bundle intentExtras = foregroundCall.getIntentExtras(); 474 if (intentExtras != null) { 475 return intentExtras.getBoolean(TelecomManager.EXTRA_CALL_EXTERNAL_RINGER, false); 476 } else { 477 return false; 478 } 479 } 480 481 private boolean isVibratorEnabled(Context context, Call call) { 482 AudioManager audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); 483 int ringerMode = audioManager.getRingerModeInternal(); 484 boolean shouldVibrate; 485 if (getVibrateWhenRinging(context)) { 486 shouldVibrate = ringerMode != AudioManager.RINGER_MODE_SILENT; 487 } else { 488 shouldVibrate = ringerMode == AudioManager.RINGER_MODE_VIBRATE; 489 } 490 491 // Technically this should be in the calling method, but it seemed a little odd to pass 492 // around a whole bunch of state just for logging purposes. 493 if (shouldVibrate) { 494 Log.addEvent(call, LogUtils.Events.START_VIBRATOR, 495 "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b", 496 mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context), 497 ringerMode, mIsVibrating); 498 } else { 499 Log.addEvent(call, LogUtils.Events.SKIP_VIBRATION, 500 "hasVibrator=%b, userRequestsVibrate=%b, ringerMode=%d, isVibrating=%b", 501 mVibrator.hasVibrator(), mSystemSettingsUtil.canVibrateWhenRinging(context), 502 ringerMode, mIsVibrating); 503 } 504 505 return shouldVibrate; 506 } 507 508 private boolean getVibrateWhenRinging(Context context) { 509 if (!mVibrator.hasVibrator()) { 510 return false; 511 } 512 return mSystemSettingsUtil.canVibrateWhenRinging(context) 513 || mSystemSettingsUtil.applyRampingRinger(context); 514 } 515 516 private RingerAttributes getRingerAttributes(Call call, boolean isHfpDeviceAttached) { 517 RingerAttributes.Builder builder = new RingerAttributes.Builder(); 518 519 LogUtils.EventTimer timer = new EventTimer(); 520 521 boolean isVolumeOverZero = mAudioManager.getStreamVolume(AudioManager.STREAM_RING) > 0; 522 timer.record("isVolumeOverZero"); 523 boolean shouldRingForContact = shouldRingForContact(call.getHandle()); 524 timer.record("shouldRingForContact"); 525 boolean isRingtonePresent = !(mRingtoneFactory.getRingtone(call) == null); 526 timer.record("getRingtone"); 527 boolean isSelfManaged = call.isSelfManaged(); 528 timer.record("isSelfManaged"); 529 boolean isSilentRingingRequested = call.isSilentRingingRequested(); 530 timer.record("isSilentRingRequested"); 531 532 boolean isRingerAudible = isVolumeOverZero && shouldRingForContact && isRingtonePresent; 533 timer.record("isRingerAudible"); 534 String inaudibleReason = ""; 535 if (!isRingerAudible) { 536 inaudibleReason = String.format( 537 "isVolumeOverZero=%s, shouldRingForContact=%s, isRingtonePresent=%s", 538 isVolumeOverZero, shouldRingForContact, isRingtonePresent); 539 } 540 541 boolean hasExternalRinger = hasExternalRinger(call); 542 timer.record("hasExternalRinger"); 543 // Don't do call waiting operations or vibration unless these are false. 544 boolean isTheaterModeOn = mSystemSettingsUtil.isTheaterModeOn(mContext); 545 timer.record("isTheaterModeOn"); 546 boolean letDialerHandleRinging = mInCallController.doesConnectedDialerSupportRinging(); 547 timer.record("letDialerHandleRinging"); 548 549 Log.i(this, "startRinging timings: " + timer); 550 boolean endEarly = isTheaterModeOn || letDialerHandleRinging || isSelfManaged || 551 hasExternalRinger || isSilentRingingRequested; 552 553 if (endEarly) { 554 Log.i(this, "Ending early -- isTheaterModeOn=%s, letDialerHandleRinging=%s, " + 555 "isSelfManaged=%s, hasExternalRinger=%s, silentRingingRequested=%s", 556 isTheaterModeOn, letDialerHandleRinging, isSelfManaged, hasExternalRinger, 557 isSilentRingingRequested); 558 } 559 560 // Acquire audio focus under any of the following conditions: 561 // 1. Should ring for contact and there's an HFP device attached 562 // 2. Volume is over zero, we should ring for the contact, and there's a audible ringtone 563 // present. 564 // 3. The call is self-managed. 565 boolean shouldAcquireAudioFocus = 566 isRingerAudible || (isHfpDeviceAttached && shouldRingForContact) || isSelfManaged; 567 568 return builder.setEndEarly(endEarly) 569 .setLetDialerHandleRinging(letDialerHandleRinging) 570 .setAcquireAudioFocus(shouldAcquireAudioFocus) 571 .setRingerAudible(isRingerAudible) 572 .setInaudibleReason(inaudibleReason) 573 .setShouldRingForContact(shouldRingForContact) 574 .setSilentRingingRequested(isSilentRingingRequested) 575 .build(); 576 } 577 578 private Handler getHandler() { 579 if (mHandler == null) { 580 HandlerThread handlerThread = new HandlerThread("Ringer"); 581 handlerThread.start(); 582 mHandler = handlerThread.getThreadHandler(); 583 } 584 return mHandler; 585 } 586 } 587