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