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