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.annotation.NonNull; 20 import android.content.Context; 21 import android.media.IAudioService; 22 import android.media.ToneGenerator; 23 import android.os.Handler; 24 import android.os.HandlerThread; 25 import android.os.UserHandle; 26 import android.telecom.CallAudioState; 27 import android.telecom.Log; 28 import android.telecom.VideoProfile; 29 import android.util.SparseArray; 30 31 import com.android.internal.annotations.VisibleForTesting; 32 import com.android.internal.util.IndentingPrintWriter; 33 import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs.Builder; 34 import com.android.server.telecom.bluetooth.BluetoothStateReceiver; 35 import com.android.server.telecom.flags.FeatureFlags; 36 37 import java.util.Collection; 38 import java.util.HashSet; 39 import java.util.Set; 40 import java.util.LinkedHashSet; 41 import java.util.concurrent.CompletableFuture; 42 import java.util.stream.Collectors; 43 44 45 public class CallAudioManager extends CallsManagerListenerBase { 46 47 public interface AudioServiceFactory { getAudioService()48 IAudioService getAudioService(); 49 } 50 51 private final String LOG_TAG = CallAudioManager.class.getSimpleName(); 52 53 private final LinkedHashSet<Call> mActiveDialingOrConnectingCalls; 54 private final LinkedHashSet<Call> mRingingCalls; 55 private final LinkedHashSet<Call> mHoldingCalls; 56 private final LinkedHashSet<Call> mAudioProcessingCalls; 57 private final Set<Call> mCalls; 58 private final SparseArray<LinkedHashSet<Call>> mCallStateToCalls; 59 60 private final CallAudioRouteAdapter mCallAudioRouteAdapter; 61 private final CallAudioModeStateMachine mCallAudioModeStateMachine; 62 private final BluetoothStateReceiver mBluetoothStateReceiver; 63 private final CallsManager mCallsManager; 64 private final InCallTonePlayer.Factory mPlayerFactory; 65 private final Ringer mRinger; 66 private final RingbackPlayer mRingbackPlayer; 67 private final DtmfLocalTonePlayer mDtmfLocalTonePlayer; 68 private final FeatureFlags mFeatureFlags; 69 70 private Call mStreamingCall; 71 private Call mForegroundCall; 72 private CompletableFuture<Boolean> mCallRingingFuture; 73 private Thread mBtIcsBindingThread; 74 private boolean mIsTonePlaying = false; 75 private boolean mIsDisconnectedTonePlaying = false; 76 private InCallTonePlayer mHoldTonePlayer; 77 private final HandlerThread mHandlerThread; 78 private final Handler mHandler; 79 CallAudioManager(CallAudioRouteAdapter callAudioRouteAdapter, CallsManager callsManager, CallAudioModeStateMachine callAudioModeStateMachine, InCallTonePlayer.Factory playerFactory, Ringer ringer, RingbackPlayer ringbackPlayer, BluetoothStateReceiver bluetoothStateReceiver, DtmfLocalTonePlayer dtmfLocalTonePlayer, FeatureFlags featureFlags)80 public CallAudioManager(CallAudioRouteAdapter callAudioRouteAdapter, 81 CallsManager callsManager, 82 CallAudioModeStateMachine callAudioModeStateMachine, 83 InCallTonePlayer.Factory playerFactory, 84 Ringer ringer, 85 RingbackPlayer ringbackPlayer, 86 BluetoothStateReceiver bluetoothStateReceiver, 87 DtmfLocalTonePlayer dtmfLocalTonePlayer, 88 FeatureFlags featureFlags) { 89 mActiveDialingOrConnectingCalls = new LinkedHashSet<>(1); 90 mRingingCalls = new LinkedHashSet<>(1); 91 mHoldingCalls = new LinkedHashSet<>(1); 92 mAudioProcessingCalls = new LinkedHashSet<>(1); 93 mStreamingCall = null; 94 mCalls = new HashSet<>(); 95 mCallStateToCalls = new SparseArray<LinkedHashSet<Call>>() {{ 96 put(CallState.CONNECTING, mActiveDialingOrConnectingCalls); 97 put(CallState.ACTIVE, mActiveDialingOrConnectingCalls); 98 put(CallState.DIALING, mActiveDialingOrConnectingCalls); 99 put(CallState.PULLING, mActiveDialingOrConnectingCalls); 100 put(CallState.RINGING, mRingingCalls); 101 put(CallState.ON_HOLD, mHoldingCalls); 102 put(CallState.SIMULATED_RINGING, mRingingCalls); 103 put(CallState.AUDIO_PROCESSING, mAudioProcessingCalls); 104 }}; 105 106 mCallAudioRouteAdapter = callAudioRouteAdapter; 107 mCallAudioModeStateMachine = callAudioModeStateMachine; 108 mCallsManager = callsManager; 109 mPlayerFactory = playerFactory; 110 mRinger = ringer; 111 mRingbackPlayer = ringbackPlayer; 112 mBluetoothStateReceiver = bluetoothStateReceiver; 113 mDtmfLocalTonePlayer = dtmfLocalTonePlayer; 114 mFeatureFlags = featureFlags; 115 mHandlerThread = new HandlerThread(this.getClass().getSimpleName()); 116 mHandlerThread.start(); 117 mHandler = new Handler(mHandlerThread.getLooper()); 118 119 mPlayerFactory.setCallAudioManager(this); 120 mCallAudioModeStateMachine.setCallAudioManager(this); 121 mCallAudioRouteAdapter.setCallAudioManager(this); 122 } 123 124 @Override onCallStateChanged(Call call, int oldState, int newState)125 public void onCallStateChanged(Call call, int oldState, int newState) { 126 if (shouldIgnoreCallForAudio(call)) { 127 // No audio management for calls in a conference, or external calls. 128 return; 129 } 130 if (oldState == newState) { 131 // State did not change, so no need to do anything. 132 return; 133 } 134 Log.i(this, "onCallStateChanged: Call state changed for TC@%s: %s -> %s", call.getId(), 135 CallState.toString(oldState), CallState.toString(newState)); 136 137 removeCallFromAllBins(call); 138 HashSet<Call> newBinForCall = getBinForCall(call); 139 if (newBinForCall != null) { 140 newBinForCall.add(call); 141 } 142 sendCallStatusToBluetoothStateReceiver(); 143 144 updateForegroundCall(); 145 if (shouldPlayDisconnectTone(oldState, newState)) { 146 playToneForDisconnectedCall(call); 147 } else { 148 if (newState == CallState.DISCONNECTED) { 149 // This call is not disconnected, but it won't generate a disconnect tone, so 150 // complete the future to ensure we unbind from BT promptly. 151 completeDisconnectToneFuture(call); 152 } 153 } 154 155 onCallLeavingState(call, oldState); 156 onCallEnteringState(call, newState); 157 } 158 159 @Override onCallAdded(Call call)160 public void onCallAdded(Call call) { 161 if (shouldIgnoreCallForAudio(call)) { 162 return; // Don't do audio handling for calls in a conference, or external calls. 163 } 164 165 addCall(call); 166 } 167 168 @Override onCallRemoved(Call call)169 public void onCallRemoved(Call call) { 170 if (mStreamingCall == call) { 171 mStreamingCall = null; 172 } 173 if (shouldIgnoreCallForAudio(call)) { 174 return; // Don't do audio handling for calls in a conference, or external calls. 175 } 176 177 removeCall(call); 178 } 179 addCall(Call call)180 private void addCall(Call call) { 181 if (mCalls.contains(call)) { 182 Log.w(LOG_TAG, "Call TC@%s is being added twice.", call.getId()); 183 return; // No guarantees that the same call won't get added twice. 184 } 185 186 Log.d(LOG_TAG, "Call added with id TC@%s in state %s", call.getId(), 187 CallState.toString(call.getState())); 188 189 HashSet<Call> newBinForCall = getBinForCall(call); 190 if (newBinForCall != null) { 191 newBinForCall.add(call); 192 } 193 updateForegroundCall(); 194 mCalls.add(call); 195 sendCallStatusToBluetoothStateReceiver(); 196 197 onCallEnteringState(call, call.getState()); 198 } 199 removeCall(Call call)200 private void removeCall(Call call) { 201 if (!mCalls.contains(call)) { 202 return; // No guarantees that the same call won't get removed twice. 203 } 204 205 Log.d(LOG_TAG, "Call removed with id TC@%s in state %s", call.getId(), 206 CallState.toString(call.getState())); 207 208 removeCallFromAllBins(call); 209 210 updateForegroundCall(); 211 mCalls.remove(call); 212 sendCallStatusToBluetoothStateReceiver(); 213 214 onCallLeavingState(call, call.getState()); 215 } 216 sendCallStatusToBluetoothStateReceiver()217 private void sendCallStatusToBluetoothStateReceiver() { 218 // We're in a call if there are calls in mCalls that are not in mAudioProcessingCalls. 219 boolean isInCall = !mAudioProcessingCalls.containsAll(mCalls); 220 mBluetoothStateReceiver.setIsInCall(isInCall); 221 } 222 223 /** 224 * Handles changes to the external state of a call. External calls which become regular calls 225 * should be tracked, and regular calls which become external should no longer be tracked. 226 * 227 * @param call The call. 228 * @param isExternalCall {@code True} if the call is now external, {@code false} if it is now 229 * a regular call. 230 */ 231 @Override onExternalCallChanged(Call call, boolean isExternalCall)232 public void onExternalCallChanged(Call call, boolean isExternalCall) { 233 if (isExternalCall) { 234 Log.d(LOG_TAG, "Removing call which became external ID %s", call.getId()); 235 removeCall(call); 236 } else if (!isExternalCall) { 237 Log.d(LOG_TAG, "Adding external call which was pulled with ID %s", call.getId()); 238 addCall(call); 239 240 if (mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(call.getVideoState())) { 241 // When pulling a video call, automatically enable the speakerphone. 242 Log.d(LOG_TAG, "Switching to speaker because external video call %s was pulled." + 243 call.getId()); 244 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 245 CallAudioRouteStateMachine.SWITCH_SPEAKER); 246 } 247 } 248 } 249 250 /** 251 * Handles the changes to the streaming state of a call. 252 * @param call The call 253 * @param isStreaming {@code true} if the call is streaming, {@code false} otherwise 254 */ 255 @Override onCallStreamingStateChanged(Call call, boolean isStreaming)256 public void onCallStreamingStateChanged(Call call, boolean isStreaming) { 257 if (isStreaming) { 258 if (mStreamingCall == null) { 259 mStreamingCall = call; 260 mCallAudioModeStateMachine.sendMessageWithArgs( 261 CallAudioModeStateMachine.START_CALL_STREAMING, 262 makeArgsForModeStateMachine()); 263 } else { 264 Log.w(LOG_TAG, "Unexpected streaming call request for call %s while call " 265 + "%s is streaming.", call.getId(), mStreamingCall.getId()); 266 } 267 } else { 268 if (mStreamingCall == call) { 269 mStreamingCall = null; 270 mCallAudioModeStateMachine.sendMessageWithArgs( 271 CallAudioModeStateMachine.STOP_CALL_STREAMING, 272 makeArgsForModeStateMachine()); 273 } else { 274 Log.w(LOG_TAG, "Unexpected call streaming stop request for call %s while this call " 275 + "is not streaming.", call.getId()); 276 } 277 } 278 } 279 280 /** 281 * Determines if {@link CallAudioManager} should do any audio routing operations for a call. 282 * We ignore child calls of a conference and external calls for audio routing purposes. 283 * 284 * @param call The call to check. 285 * @return {@code true} if the call should be ignored for audio routing, {@code false} 286 * otherwise 287 */ shouldIgnoreCallForAudio(Call call)288 private boolean shouldIgnoreCallForAudio(Call call) { 289 return call.getParentCall() != null || call.isExternalCall(); 290 } 291 292 @Override onIncomingCallAnswered(Call call)293 public void onIncomingCallAnswered(Call call) { 294 if (!mCalls.contains(call)) { 295 return; 296 } 297 298 // Turn off mute when a new incoming call is answered iff it's not a handover. 299 if (!call.isHandoverInProgress()) { 300 mute(false /* shouldMute */); 301 } 302 303 maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call); 304 } 305 306 @Override onSessionModifyRequestReceived(Call call, VideoProfile videoProfile)307 public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) { 308 if (videoProfile == null) { 309 return; 310 } 311 312 if (call != mForegroundCall) { 313 // We only play tones for foreground calls. 314 return; 315 } 316 317 int previousVideoState = call.getVideoState(); 318 int newVideoState = videoProfile.getVideoState(); 319 Log.v(this, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile 320 .videoStateToString(newVideoState)); 321 322 boolean isUpgradeRequest = !VideoProfile.isReceptionEnabled(previousVideoState) && 323 VideoProfile.isReceptionEnabled(newVideoState); 324 325 if (isUpgradeRequest) { 326 mPlayerFactory.createPlayer(call, InCallTonePlayer.TONE_VIDEO_UPGRADE).startTone(); 327 } 328 } 329 playRttUpgradeTone(Call call)330 public void playRttUpgradeTone(Call call) { 331 if (call != mForegroundCall) { 332 // We only play tones for foreground calls. 333 return; 334 } 335 mPlayerFactory.createPlayer(call, InCallTonePlayer.TONE_RTT_REQUEST).startTone(); 336 } 337 338 /** 339 * Play or stop a call hold tone for a call. Triggered via 340 * {@link Connection#sendConnectionEvent(String)} when the 341 * {@link Connection#EVENT_ON_HOLD_TONE_START} event or 342 * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the 343 * 344 * @param call The call which requested the hold tone. 345 */ 346 @Override onHoldToneRequested(Call call)347 public void onHoldToneRequested(Call call) { 348 maybePlayHoldTone(call); 349 } 350 351 @Override onIsVoipAudioModeChanged(Call call)352 public void onIsVoipAudioModeChanged(Call call) { 353 if (call != mForegroundCall) { 354 return; 355 } 356 mCallAudioModeStateMachine.sendMessageWithArgs( 357 CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, 358 makeArgsForModeStateMachine()); 359 } 360 361 @Override onRingbackRequested(Call call, boolean shouldRingback)362 public void onRingbackRequested(Call call, boolean shouldRingback) { 363 if (call == mForegroundCall && shouldRingback) { 364 mRingbackPlayer.startRingbackForCall(call); 365 } else { 366 mRingbackPlayer.stopRingbackForCall(call); 367 } 368 } 369 370 @Override onIncomingCallRejected(Call call, boolean rejectWithMessage, String message)371 public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String message) { 372 maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call); 373 } 374 375 @Override onIsConferencedChanged(Call call)376 public void onIsConferencedChanged(Call call) { 377 // This indicates a conferencing change, which shouldn't impact any audio mode stuff. 378 Call parentCall = call.getParentCall(); 379 if (parentCall == null) { 380 // Indicates that the call should be tracked for audio purposes. Treat it as if it were 381 // just added. 382 Log.i(LOG_TAG, "Call TC@" + call.getId() + " left conference and will" + 383 " now be tracked by CallAudioManager."); 384 onCallAdded(call); 385 } else { 386 // The call joined a conference, so stop tracking it. 387 removeCallFromAllBins(call); 388 updateForegroundCall(); 389 mCalls.remove(call); 390 } 391 } 392 393 @Override onConnectionServiceChanged(Call call, ConnectionServiceWrapper oldCs, ConnectionServiceWrapper newCs)394 public void onConnectionServiceChanged(Call call, ConnectionServiceWrapper oldCs, 395 ConnectionServiceWrapper newCs) { 396 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 397 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); 398 } 399 400 @Override onVideoStateChanged(Call call, int previousVideoState, int newVideoState)401 public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) { 402 if (call != getForegroundCall()) { 403 Log.d(LOG_TAG, "Ignoring video state change from %s to %s for call %s -- not " + 404 "foreground.", VideoProfile.videoStateToString(previousVideoState), 405 VideoProfile.videoStateToString(newVideoState), call.getId()); 406 return; 407 } 408 409 if (!VideoProfile.isVideo(previousVideoState) && 410 mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(newVideoState)) { 411 Log.d(LOG_TAG, "Switching to speaker because call %s transitioned video state from %s" + 412 " to %s", call.getId(), VideoProfile.videoStateToString(previousVideoState), 413 VideoProfile.videoStateToString(newVideoState)); 414 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 415 CallAudioRouteStateMachine.SWITCH_SPEAKER); 416 } 417 } 418 getCallAudioState()419 public CallAudioState getCallAudioState() { 420 return mCallAudioRouteAdapter.getCurrentCallAudioState(); 421 } 422 getPossiblyHeldForegroundCall()423 public Call getPossiblyHeldForegroundCall() { 424 return mForegroundCall; 425 } 426 getForegroundCall()427 public Call getForegroundCall() { 428 if (mForegroundCall != null && mForegroundCall.getState() != CallState.ON_HOLD) { 429 return mForegroundCall; 430 } 431 return null; 432 } 433 434 @VisibleForTesting toggleMute()435 public void toggleMute() { 436 // Don't mute if there are any emergency calls. 437 if (mCallsManager.isInEmergencyCall()) { 438 Log.v(this, "ignoring toggleMute for emergency call"); 439 return; 440 } 441 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 442 CallAudioRouteStateMachine.TOGGLE_MUTE); 443 } 444 445 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) onRingerModeChange()446 public void onRingerModeChange() { 447 if (mFeatureFlags.ensureInCarRinging()) { 448 // Stop the current ringtone before attempting to start the new ringtone: 449 stopRinging(); 450 } 451 mCallAudioModeStateMachine.sendMessageWithArgs( 452 CallAudioModeStateMachine.RINGER_MODE_CHANGE, makeArgsForModeStateMachine()); 453 } 454 455 @VisibleForTesting mute(boolean shouldMute)456 public void mute(boolean shouldMute) { 457 Log.v(this, "mute, shouldMute: %b", shouldMute); 458 459 // Don't mute if there are any emergency calls. 460 if (mCallsManager.isInEmergencyCall()) { 461 shouldMute = false; 462 Log.v(this, "ignoring mute for emergency call"); 463 } 464 465 mCallAudioRouteAdapter.sendMessageWithSessionInfo(shouldMute 466 ? CallAudioRouteStateMachine.MUTE_ON : CallAudioRouteStateMachine.MUTE_OFF); 467 } 468 469 /** 470 * Changed the audio route, for example from earpiece to speaker phone. 471 * 472 * @param route The new audio route to use. See {@link CallAudioState}. 473 * @param bluetoothAddress the address of the desired bluetooth device, if route is 474 * {@link CallAudioState#ROUTE_BLUETOOTH}. 475 */ 476 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PROTECTED) setAudioRoute(int route, String bluetoothAddress)477 public void setAudioRoute(int route, String bluetoothAddress) { 478 Log.v(this, "setAudioRoute, route: %s", CallAudioState.audioRouteToString(route)); 479 switch (route) { 480 case CallAudioState.ROUTE_BLUETOOTH: 481 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 482 CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH, 0, bluetoothAddress); 483 return; 484 case CallAudioState.ROUTE_SPEAKER: 485 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 486 CallAudioRouteStateMachine.USER_SWITCH_SPEAKER); 487 return; 488 case CallAudioState.ROUTE_WIRED_HEADSET: 489 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 490 CallAudioRouteStateMachine.USER_SWITCH_HEADSET); 491 return; 492 case CallAudioState.ROUTE_EARPIECE: 493 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 494 CallAudioRouteStateMachine.USER_SWITCH_EARPIECE); 495 return; 496 case CallAudioState.ROUTE_WIRED_OR_EARPIECE: 497 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 498 CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE, 499 CallAudioRouteStateMachine.NO_INCLUDE_BLUETOOTH_IN_BASELINE); 500 return; 501 default: 502 Log.w(this, "InCallService requested an invalid audio route: %d", route); 503 } 504 } 505 506 /** 507 * Switch call audio routing to the baseline route, including bluetooth headsets if there are 508 * any connected. 509 */ switchBaseline()510 void switchBaseline() { 511 Log.i(this, "switchBaseline"); 512 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 513 CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE, 514 CallAudioRouteStateMachine.INCLUDE_BLUETOOTH_IN_BASELINE); 515 } 516 silenceRingers(Context context, UserHandle callingUser, boolean hasCrossUserPermission)517 Set<UserHandle> silenceRingers(Context context, UserHandle callingUser, 518 boolean hasCrossUserPermission) { 519 // Store all users from calls that were silenced so that we can silence the 520 // InCallServices which are associated with those users. 521 Set<UserHandle> userHandles = new HashSet<>(); 522 boolean allCallSilenced = true; 523 synchronized (mCallsManager.getLock()) { 524 for (Call call : mRingingCalls) { 525 UserHandle userFromCall = call.getAssociatedUser(); 526 // Do not try to silence calls when calling user is different from the phone account 527 // user, the account does not have CAPABILITY_MULTI_USER enabled, or if the user 528 // does not have the INTERACT_ACROSS_USERS permission enabled. 529 if (!hasCrossUserPermission && !mCallsManager 530 .isCallVisibleForUser(call, callingUser)) { 531 allCallSilenced = false; 532 continue; 533 } 534 userHandles.add(userFromCall); 535 call.silence(); 536 } 537 538 // If all the calls were silenced, we can stop the ringer. 539 if (allCallSilenced) { 540 mRinger.stopRinging(); 541 mRinger.stopCallWaiting(); 542 } 543 } 544 return userHandles; 545 } 546 isRingtonePlaying()547 public boolean isRingtonePlaying() { 548 return mRinger.isRinging(); 549 } 550 551 @VisibleForTesting startRinging()552 public boolean startRinging() { 553 synchronized (mCallsManager.getLock()) { 554 Call localForegroundCall = mForegroundCall; 555 boolean result = mRinger.startRinging(localForegroundCall, 556 mCallAudioRouteAdapter.isHfpDeviceAvailable()); 557 if (result) { 558 localForegroundCall.setStartRingTime(); 559 } 560 return result; 561 } 562 } 563 564 @VisibleForTesting startCallWaiting(String reason)565 public void startCallWaiting(String reason) { 566 synchronized (mCallsManager.getLock()) { 567 if (mRingingCalls.size() == 1) { 568 mRinger.startCallWaiting(mRingingCalls.iterator().next(), reason); 569 } 570 } 571 } 572 573 @VisibleForTesting stopRinging()574 public void stopRinging() { 575 synchronized (mCallsManager.getLock()) { 576 mRinger.stopRinging(); 577 } 578 } 579 580 @VisibleForTesting stopCallWaiting()581 public void stopCallWaiting() { 582 synchronized (mCallsManager.getLock()) { 583 mRinger.stopCallWaiting(); 584 } 585 } 586 587 @VisibleForTesting setCallAudioRouteFocusState(int focusState)588 public void setCallAudioRouteFocusState(int focusState) { 589 if (mFeatureFlags.useRefactoredAudioRouteSwitching()) { 590 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 591 CallAudioRouteStateMachine.SWITCH_FOCUS, focusState, 0); 592 } else { 593 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 594 CallAudioRouteStateMachine.SWITCH_FOCUS, focusState); 595 } 596 } 597 setCallAudioRouteFocusStateForEndTone()598 public void setCallAudioRouteFocusStateForEndTone() { 599 if (mFeatureFlags.useRefactoredAudioRouteSwitching()) { 600 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 601 CallAudioRouteStateMachine.SWITCH_FOCUS, 602 CallAudioRouteStateMachine.ACTIVE_FOCUS, 1); 603 } else { 604 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 605 CallAudioRouteStateMachine.SWITCH_FOCUS, 606 CallAudioRouteStateMachine.ACTIVE_FOCUS); 607 } 608 } 609 notifyAudioOperationsComplete()610 public void notifyAudioOperationsComplete() { 611 mCallAudioModeStateMachine.sendMessageWithArgs( 612 CallAudioModeStateMachine.AUDIO_OPERATIONS_COMPLETE, makeArgsForModeStateMachine()); 613 } 614 615 @VisibleForTesting getCallAudioRouteAdapter()616 public CallAudioRouteAdapter getCallAudioRouteAdapter() { 617 return mCallAudioRouteAdapter; 618 } 619 620 @VisibleForTesting getCallAudioModeStateMachine()621 public CallAudioModeStateMachine getCallAudioModeStateMachine() { 622 return mCallAudioModeStateMachine; 623 } 624 dump(IndentingPrintWriter pw)625 void dump(IndentingPrintWriter pw) { 626 pw.println("All calls:"); 627 pw.increaseIndent(); 628 dumpCallsInCollection(pw, mCalls); 629 pw.decreaseIndent(); 630 631 pw.println("Active dialing, or connecting calls:"); 632 pw.increaseIndent(); 633 dumpCallsInCollection(pw, mActiveDialingOrConnectingCalls); 634 pw.decreaseIndent(); 635 636 pw.println("Ringing calls:"); 637 pw.increaseIndent(); 638 dumpCallsInCollection(pw, mRingingCalls); 639 pw.decreaseIndent(); 640 641 pw.println("Holding calls:"); 642 pw.increaseIndent(); 643 dumpCallsInCollection(pw, mHoldingCalls); 644 pw.decreaseIndent(); 645 646 pw.println("Foreground call:"); 647 pw.println(mForegroundCall); 648 649 pw.println("CallAudioModeStateMachine:"); 650 pw.increaseIndent(); 651 mCallAudioModeStateMachine.dump(pw); 652 pw.decreaseIndent(); 653 654 pw.println("mCallAudioRouteAdapter:"); 655 pw.increaseIndent(); 656 mCallAudioRouteAdapter.dump(pw); 657 pw.decreaseIndent(); 658 659 pw.println("BluetoothDeviceManager:"); 660 pw.increaseIndent(); 661 if (mBluetoothStateReceiver.getBluetoothDeviceManager() != null) { 662 mBluetoothStateReceiver.getBluetoothDeviceManager().dump(pw); 663 } 664 pw.decreaseIndent(); 665 } 666 667 @VisibleForTesting setIsTonePlaying(Call call, boolean isTonePlaying)668 public void setIsTonePlaying(Call call, boolean isTonePlaying) { 669 Log.i(this, "setIsTonePlaying; isTonePlaying=%b", isTonePlaying); 670 mIsTonePlaying = isTonePlaying; 671 mCallAudioModeStateMachine.sendMessageWithArgs( 672 isTonePlaying ? CallAudioModeStateMachine.TONE_STARTED_PLAYING 673 : CallAudioModeStateMachine.TONE_STOPPED_PLAYING, 674 makeArgsForModeStateMachine()); 675 676 if (!isTonePlaying && mIsDisconnectedTonePlaying) { 677 mCallsManager.onDisconnectedTonePlaying(call, false); 678 mIsDisconnectedTonePlaying = false; 679 } 680 } 681 onCallLeavingState(Call call, int state)682 private void onCallLeavingState(Call call, int state) { 683 switch (state) { 684 case CallState.ACTIVE: 685 case CallState.CONNECTING: 686 onCallLeavingActiveDialingOrConnecting(); 687 break; 688 case CallState.RINGING: 689 case CallState.SIMULATED_RINGING: 690 case CallState.ANSWERED: 691 onCallLeavingRinging(); 692 break; 693 case CallState.ON_HOLD: 694 onCallLeavingHold(); 695 break; 696 case CallState.PULLING: 697 onCallLeavingActiveDialingOrConnecting(); 698 break; 699 case CallState.DIALING: 700 stopRingbackForCall(call); 701 onCallLeavingActiveDialingOrConnecting(); 702 break; 703 case CallState.AUDIO_PROCESSING: 704 onCallLeavingAudioProcessing(); 705 break; 706 } 707 } 708 onCallEnteringState(Call call, int state)709 private void onCallEnteringState(Call call, int state) { 710 switch (state) { 711 case CallState.ACTIVE: 712 case CallState.CONNECTING: 713 onCallEnteringActiveDialingOrConnecting(); 714 break; 715 case CallState.RINGING: 716 case CallState.SIMULATED_RINGING: 717 onCallEnteringRinging(); 718 break; 719 case CallState.ON_HOLD: 720 onCallEnteringHold(); 721 break; 722 case CallState.PULLING: 723 onCallEnteringActiveDialingOrConnecting(); 724 break; 725 case CallState.DIALING: 726 onCallEnteringActiveDialingOrConnecting(); 727 playRingbackForCall(call); 728 break; 729 case CallState.ANSWERED: 730 if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) { 731 onCallEnteringActiveDialingOrConnecting(); 732 } 733 break; 734 case CallState.AUDIO_PROCESSING: 735 onCallEnteringAudioProcessing(); 736 break; 737 } 738 } 739 onCallLeavingAudioProcessing()740 private void onCallLeavingAudioProcessing() { 741 if (mAudioProcessingCalls.size() == 0) { 742 mCallAudioModeStateMachine.sendMessageWithArgs( 743 CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS, 744 makeArgsForModeStateMachine()); 745 } 746 } 747 onCallEnteringAudioProcessing()748 private void onCallEnteringAudioProcessing() { 749 if (mAudioProcessingCalls.size() == 1) { 750 mCallAudioModeStateMachine.sendMessageWithArgs( 751 CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL, 752 makeArgsForModeStateMachine()); 753 } 754 } 755 onCallLeavingActiveDialingOrConnecting()756 private void onCallLeavingActiveDialingOrConnecting() { 757 if (mActiveDialingOrConnectingCalls.size() == 0) { 758 mCallAudioModeStateMachine.sendMessageWithArgs( 759 CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, 760 makeArgsForModeStateMachine()); 761 } 762 } 763 onCallLeavingRinging()764 private void onCallLeavingRinging() { 765 if (mRingingCalls.size() == 0) { 766 mCallAudioModeStateMachine.sendMessageWithArgs( 767 CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, 768 makeArgsForModeStateMachine()); 769 } 770 } 771 onCallLeavingHold()772 private void onCallLeavingHold() { 773 if (mHoldingCalls.size() == 0) { 774 mCallAudioModeStateMachine.sendMessageWithArgs( 775 CallAudioModeStateMachine.NO_MORE_HOLDING_CALLS, 776 makeArgsForModeStateMachine()); 777 } 778 } 779 onCallEnteringActiveDialingOrConnecting()780 private void onCallEnteringActiveDialingOrConnecting() { 781 if (mActiveDialingOrConnectingCalls.size() == 1) { 782 mCallAudioModeStateMachine.sendMessageWithArgs( 783 CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, 784 makeArgsForModeStateMachine()); 785 } 786 } 787 onCallEnteringRinging()788 private void onCallEnteringRinging() { 789 if (mRingingCalls.size() == 1) { 790 Log.i(this, "onCallEnteringRinging: mFeatureFlags.separatelyBindToBtIncallService() ? %s", 791 mFeatureFlags.separatelyBindToBtIncallService()); 792 Log.i(this, "onCallEnteringRinging: mRingingCalls.getFirst().getBtIcsFuture() = %s", 793 mRingingCalls.getFirst().getBtIcsFuture()); 794 if (mFeatureFlags.separatelyBindToBtIncallService() 795 && mRingingCalls.getFirst().getBtIcsFuture() != null) { 796 mCallRingingFuture = mRingingCalls.getFirst().getBtIcsFuture() 797 .thenComposeAsync((completed) -> { 798 mCallAudioModeStateMachine.sendMessageWithArgs( 799 CallAudioModeStateMachine.NEW_RINGING_CALL, 800 makeArgsForModeStateMachine()); 801 return CompletableFuture.completedFuture(completed); 802 }, new LoggedHandlerExecutor(mHandler, "CAM.oCER", mCallsManager.getLock())) 803 .exceptionally((throwable) -> { 804 Log.e(this, throwable, "Error while executing BT ICS future"); 805 // Fallback on performing computation on a separate thread. 806 handleBtBindingWaitFallback(); 807 return null; 808 }); 809 } else { 810 mCallAudioModeStateMachine.sendMessageWithArgs( 811 CallAudioModeStateMachine.NEW_RINGING_CALL, 812 makeArgsForModeStateMachine()); 813 } 814 } 815 } 816 handleBtBindingWaitFallback()817 private void handleBtBindingWaitFallback() { 818 // Wait until the BT ICS binding completed to request further audio route change 819 mBtIcsBindingThread = new Thread(() -> { 820 mRingingCalls.getFirst().waitForBtIcs(); 821 mCallAudioModeStateMachine.sendMessageWithArgs( 822 CallAudioModeStateMachine.NEW_RINGING_CALL, 823 makeArgsForModeStateMachine()); 824 }); 825 mBtIcsBindingThread.start(); 826 } 827 onCallEnteringHold()828 private void onCallEnteringHold() { 829 if (mHoldingCalls.size() == 1) { 830 mCallAudioModeStateMachine.sendMessageWithArgs( 831 CallAudioModeStateMachine.NEW_HOLDING_CALL, 832 makeArgsForModeStateMachine()); 833 } 834 } 835 updateForegroundCall()836 private void updateForegroundCall() { 837 Call oldForegroundCall = mForegroundCall; 838 839 if (mActiveDialingOrConnectingCalls.size() > 0) { 840 // Give preference for connecting calls over active/dialing for foreground-ness. 841 Call possibleConnectingCall = null; 842 for (Call call : mActiveDialingOrConnectingCalls) { 843 if (call.getState() == CallState.CONNECTING) { 844 possibleConnectingCall = call; 845 } 846 } 847 if (mFeatureFlags.ensureAudioModeUpdatesOnForegroundCallChange()) { 848 // Prefer a connecting call 849 if (possibleConnectingCall != null) { 850 mForegroundCall = possibleConnectingCall; 851 } else { 852 // Next, prefer an active or dialing call which is not in the process of being 853 // disconnected. 854 mForegroundCall = mActiveDialingOrConnectingCalls 855 .stream() 856 .filter(c -> (c.getState() == CallState.ACTIVE 857 || c.getState() == CallState.DIALING) 858 && !c.isLocallyDisconnecting()) 859 .findFirst() 860 // If we can't find one, then just fall back to the first one. 861 .orElse(mActiveDialingOrConnectingCalls.iterator().next()); 862 } 863 } else { 864 // Legacy (buggy) behavior. 865 mForegroundCall = possibleConnectingCall == null ? 866 mActiveDialingOrConnectingCalls.iterator().next() : possibleConnectingCall; 867 } 868 } else if (mRingingCalls.size() > 0) { 869 mForegroundCall = mRingingCalls.iterator().next(); 870 } else if (mHoldingCalls.size() > 0) { 871 mForegroundCall = mHoldingCalls.iterator().next(); 872 } else { 873 mForegroundCall = null; 874 } 875 Log.i(this, "updateForegroundCall; oldFg=%s, newFg=%s, aDC=%s, ring=%s, hold=%s", 876 (oldForegroundCall == null ? "none" : oldForegroundCall.getId()), 877 (mForegroundCall == null ? "none" : mForegroundCall.getId()), 878 mActiveDialingOrConnectingCalls.stream().map(c -> c.getId()).collect( 879 Collectors.joining(",")), 880 mRingingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(",")), 881 mHoldingCalls.stream().map(c -> c.getId()).collect(Collectors.joining(",")) 882 ); 883 if (mForegroundCall != oldForegroundCall) { 884 mCallAudioRouteAdapter.sendMessageWithSessionInfo( 885 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); 886 887 if (mForegroundCall != null 888 && mFeatureFlags.ensureAudioModeUpdatesOnForegroundCallChange()) { 889 // Ensure the voip audio mode for the new foreground call is taken into account. 890 mCallAudioModeStateMachine.sendMessageWithArgs( 891 CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, 892 makeArgsForModeStateMachine()); 893 } 894 mDtmfLocalTonePlayer.onForegroundCallChanged(oldForegroundCall, mForegroundCall); 895 maybePlayHoldTone(oldForegroundCall); 896 } 897 } 898 899 @NonNull makeArgsForModeStateMachine()900 private CallAudioModeStateMachine.MessageArgs makeArgsForModeStateMachine() { 901 return new Builder() 902 .setHasActiveOrDialingCalls(mActiveDialingOrConnectingCalls.size() > 0) 903 .setHasRingingCalls(mRingingCalls.size() > 0) 904 .setHasHoldingCalls(mHoldingCalls.size() > 0) 905 .setHasAudioProcessingCalls(mAudioProcessingCalls.size() > 0) 906 .setIsTonePlaying(mIsTonePlaying) 907 .setIsStreaming((mStreamingCall != null) && (!mStreamingCall.isDisconnected())) 908 .setForegroundCallIsVoip( 909 mForegroundCall != null && isCallVoip(mForegroundCall)) 910 .setSession(Log.createSubsession()).build(); 911 } 912 913 /** 914 * Determines if a {@link Call} is a VOIP call for audio purposes. 915 * For top level calls, we get this from {@link Call#getIsVoipAudioMode()}. A {@link Call} 916 * representing a {@link android.telecom.Conference}, however, has no means of specifying that 917 * it is a VOIP conference, so we will get that attribute from one of the children. 918 * @param call The call. 919 * @return {@code true} if the call is a VOIP call, {@code false} if is a SIM call. 920 */ 921 @VisibleForTesting isCallVoip(Call call)922 public boolean isCallVoip(Call call) { 923 if (call.isConference() && call.getChildCalls() != null 924 && call.getChildCalls().size() > 0 ) { 925 // If this is a conference with children, we can get the VOIP audio mode attribute from 926 // one of the children. The Conference doesn't have a VOIP audio mode property, so we 927 // need to infer from the first child. 928 Call firstChild = call.getChildCalls().get(0); 929 return firstChild.getIsVoipAudioMode(); 930 } 931 return call.getIsVoipAudioMode(); 932 } 933 getBinForCall(Call call)934 private HashSet<Call> getBinForCall(Call call) { 935 if (call.getState() == CallState.ANSWERED) { 936 // If the call has the speed-up-mt-audio capability, treat answered state as active 937 // for audio purposes. 938 if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) { 939 return mActiveDialingOrConnectingCalls; 940 } 941 return mRingingCalls; 942 } 943 return mCallStateToCalls.get(call.getState()); 944 } 945 removeCallFromAllBins(Call call)946 private void removeCallFromAllBins(Call call) { 947 for (int i = 0; i < mCallStateToCalls.size(); i++) { 948 mCallStateToCalls.valueAt(i).remove(call); 949 } 950 } 951 playToneForDisconnectedCall(Call call)952 private void playToneForDisconnectedCall(Call call) { 953 // If this call is being disconnected as a result of being handed over to another call, 954 // we will not play a disconnect tone. 955 if (call.isHandoverInProgress()) { 956 Log.i(LOG_TAG, "Omitting tone because %s is being handed over.", call); 957 completeDisconnectToneFuture(call); 958 return; 959 } 960 961 if (mForegroundCall != null && call != mForegroundCall && mCalls.size() > 1) { 962 Log.v(LOG_TAG, "Omitting tone because we are not foreground" + 963 " and there is another call."); 964 completeDisconnectToneFuture(call); 965 return; 966 } 967 968 if (call.getDisconnectCause() != null) { 969 int toneToPlay = InCallTonePlayer.TONE_INVALID; 970 971 Log.v(this, "Disconnect cause: %s.", call.getDisconnectCause()); 972 973 switch(call.getDisconnectCause().getTone()) { 974 case ToneGenerator.TONE_SUP_BUSY: 975 toneToPlay = InCallTonePlayer.TONE_BUSY; 976 break; 977 case ToneGenerator.TONE_SUP_CONGESTION: 978 toneToPlay = InCallTonePlayer.TONE_CONGESTION; 979 break; 980 case ToneGenerator.TONE_CDMA_REORDER: 981 toneToPlay = InCallTonePlayer.TONE_REORDER; 982 break; 983 case ToneGenerator.TONE_CDMA_ABBR_INTERCEPT: 984 toneToPlay = InCallTonePlayer.TONE_INTERCEPT; 985 break; 986 case ToneGenerator.TONE_CDMA_CALLDROP_LITE: 987 toneToPlay = InCallTonePlayer.TONE_CDMA_DROP; 988 break; 989 case ToneGenerator.TONE_SUP_ERROR: 990 toneToPlay = InCallTonePlayer.TONE_UNOBTAINABLE_NUMBER; 991 break; 992 case ToneGenerator.TONE_PROP_PROMPT: 993 toneToPlay = InCallTonePlayer.TONE_CALL_ENDED; 994 break; 995 } 996 997 Log.d(this, "Found a disconnected call with tone to play %d.", toneToPlay); 998 999 if (toneToPlay != InCallTonePlayer.TONE_INVALID) { 1000 boolean didToneStart = mPlayerFactory.createPlayer(call, toneToPlay).startTone(); 1001 if (didToneStart) { 1002 mCallsManager.onDisconnectedTonePlaying(call, true); 1003 mIsDisconnectedTonePlaying = true; 1004 } 1005 } else { 1006 completeDisconnectToneFuture(call); 1007 } 1008 } 1009 } 1010 playRingbackForCall(Call call)1011 private void playRingbackForCall(Call call) { 1012 if (call == mForegroundCall && call.isRingbackRequested()) { 1013 mRingbackPlayer.startRingbackForCall(call); 1014 } 1015 } 1016 stopRingbackForCall(Call call)1017 private void stopRingbackForCall(Call call) { 1018 mRingbackPlayer.stopRingbackForCall(call); 1019 } 1020 1021 /** 1022 * Determines if a hold tone should be played and then starts or stops it accordingly. 1023 */ maybePlayHoldTone(Call call)1024 private void maybePlayHoldTone(Call call) { 1025 if (shouldPlayHoldTone()) { 1026 if (mHoldTonePlayer == null) { 1027 mHoldTonePlayer = mPlayerFactory.createPlayer(call, 1028 InCallTonePlayer.TONE_CALL_WAITING); 1029 mHoldTonePlayer.startTone(); 1030 } 1031 } else { 1032 if (mHoldTonePlayer != null) { 1033 mHoldTonePlayer.stopTone(); 1034 mHoldTonePlayer = null; 1035 } 1036 } 1037 } 1038 1039 /** 1040 * Determines if a hold tone should be played. 1041 * A hold tone should be played only if foreground call is equals with call which is 1042 * remotely held. 1043 * 1044 * @return {@code true} if the the hold tone should be played, {@code false} otherwise. 1045 */ shouldPlayHoldTone()1046 private boolean shouldPlayHoldTone() { 1047 Call foregroundCall = getForegroundCall(); 1048 // If there is no foreground call, no hold tone should play. 1049 if (foregroundCall == null) { 1050 return false; 1051 } 1052 1053 // If another call is ringing, no hold tone should play. 1054 if (mCallsManager.hasRingingCall()) { 1055 return false; 1056 } 1057 1058 // If the foreground call isn't active, no hold tone should play. This might happen, for 1059 // example, if the user puts a remotely held call on hold itself. 1060 if (!foregroundCall.isActive()) { 1061 return false; 1062 } 1063 1064 return foregroundCall.isRemotelyHeld(); 1065 } 1066 dumpCallsInCollection(IndentingPrintWriter pw, Collection<Call> calls)1067 private void dumpCallsInCollection(IndentingPrintWriter pw, Collection<Call> calls) { 1068 for (Call call : calls) { 1069 if (call != null) pw.println(call.getId()); 1070 } 1071 } 1072 maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(Call call)1073 private void maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(Call call) { 1074 // Check to see if the call being answered/rejected is the only ringing call, since this 1075 // will be called before the connection service acknowledges the state change. 1076 synchronized (mCallsManager.getLock()) { 1077 if (mRingingCalls.size() == 0 || 1078 (mRingingCalls.size() == 1 && call == mRingingCalls.iterator().next())) { 1079 mRinger.stopRinging(); 1080 mRinger.stopCallWaiting(); 1081 } 1082 } 1083 } 1084 shouldPlayDisconnectTone(int oldState, int newState)1085 private boolean shouldPlayDisconnectTone(int oldState, int newState) { 1086 if (newState != CallState.DISCONNECTED) { 1087 return false; 1088 } 1089 return oldState == CallState.ACTIVE || 1090 oldState == CallState.DIALING || 1091 oldState == CallState.ON_HOLD; 1092 } 1093 completeDisconnectToneFuture(Call call)1094 private void completeDisconnectToneFuture(Call call) { 1095 CompletableFuture<Void> disconnectedToneFuture = mCallsManager.getInCallController() 1096 .getDisconnectedToneBtFutures().get(call.getId()); 1097 if (disconnectedToneFuture != null) { 1098 Log.i(this, 1099 "completeDisconnectToneFuture: completing deferred disconnect tone future for" 1100 + " call %s", 1101 call.getId()); 1102 disconnectedToneFuture.complete(null); 1103 } 1104 // Make sure we schedule the unbinding of the BT ICS once the disconnected tone future has 1105 // been completed. 1106 mCallsManager.getInCallController().maybeScheduleBtUnbind(call); 1107 } 1108 1109 @VisibleForTesting getTrackedCalls()1110 public Set<Call> getTrackedCalls() { 1111 return mCalls; 1112 } 1113 1114 @VisibleForTesting getCallStateToCalls()1115 public SparseArray<LinkedHashSet<Call>> getCallStateToCalls() { 1116 return mCallStateToCalls; 1117 } 1118 1119 @VisibleForTesting getCallRingingFuture()1120 public CompletableFuture<Boolean> getCallRingingFuture() { 1121 return mCallRingingFuture; 1122 } 1123 } 1124