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( 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 maybeStartVibration(Call foregroundCall, boolean shouldRingForContact, VibrationEffect effect, boolean isVibrationEnabled, boolean isRingerAudible)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 getVibrationEffectForCall(RingtoneFactory factory, Call call)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 startCallWaiting(Call call)389 public void startCallWaiting(Call call) { 390 startCallWaiting(call, null); 391 } 392 startCallWaiting(Call call, String reason)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 stopRinging()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 stopCallWaiting()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 isRinging()456 public boolean isRinging() { 457 return mRingtonePlayer.isPlaying(); 458 } 459 shouldRingForContact(Uri contactUri)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 hasExternalRinger(Call foregroundCall)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 isVibratorEnabled(Context context, Call call)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 getVibrateWhenRinging(Context context)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 getRingerAttributes(Call call, boolean isHfpDeviceAttached)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 getHandler()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