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.media.IAudioService; 21 import android.media.ToneGenerator; 22 import android.telecom.CallAudioState; 23 import android.telecom.Log; 24 import android.telecom.VideoProfile; 25 import android.util.SparseArray; 26 27 import com.android.internal.annotations.VisibleForTesting; 28 import com.android.internal.util.IndentingPrintWriter; 29 import com.android.server.telecom.CallAudioModeStateMachine.MessageArgs.Builder; 30 import com.android.server.telecom.bluetooth.BluetoothStateReceiver; 31 32 import java.util.Collection; 33 import java.util.HashSet; 34 import java.util.Set; 35 import java.util.LinkedHashSet; 36 37 public class CallAudioManager extends CallsManagerListenerBase { 38 39 public interface AudioServiceFactory { getAudioService()40 IAudioService getAudioService(); 41 } 42 43 private final String LOG_TAG = CallAudioManager.class.getSimpleName(); 44 45 private final LinkedHashSet<Call> mActiveDialingOrConnectingCalls; 46 private final LinkedHashSet<Call> mRingingCalls; 47 private final LinkedHashSet<Call> mHoldingCalls; 48 private final LinkedHashSet<Call> mAudioProcessingCalls; 49 private final Set<Call> mCalls; 50 private final SparseArray<LinkedHashSet<Call>> mCallStateToCalls; 51 52 private final CallAudioRouteStateMachine mCallAudioRouteStateMachine; 53 private final CallAudioModeStateMachine mCallAudioModeStateMachine; 54 private final BluetoothStateReceiver mBluetoothStateReceiver; 55 private final CallsManager mCallsManager; 56 private final InCallTonePlayer.Factory mPlayerFactory; 57 private final Ringer mRinger; 58 private final RingbackPlayer mRingbackPlayer; 59 private final DtmfLocalTonePlayer mDtmfLocalTonePlayer; 60 61 private Call mForegroundCall; 62 private boolean mIsTonePlaying = false; 63 private boolean mIsDisconnectedTonePlaying = false; 64 private InCallTonePlayer mHoldTonePlayer; 65 CallAudioManager(CallAudioRouteStateMachine callAudioRouteStateMachine, CallsManager callsManager, CallAudioModeStateMachine callAudioModeStateMachine, InCallTonePlayer.Factory playerFactory, Ringer ringer, RingbackPlayer ringbackPlayer, BluetoothStateReceiver bluetoothStateReceiver, DtmfLocalTonePlayer dtmfLocalTonePlayer)66 public CallAudioManager(CallAudioRouteStateMachine callAudioRouteStateMachine, 67 CallsManager callsManager, 68 CallAudioModeStateMachine callAudioModeStateMachine, 69 InCallTonePlayer.Factory playerFactory, 70 Ringer ringer, 71 RingbackPlayer ringbackPlayer, 72 BluetoothStateReceiver bluetoothStateReceiver, 73 DtmfLocalTonePlayer dtmfLocalTonePlayer) { 74 mActiveDialingOrConnectingCalls = new LinkedHashSet<>(1); 75 mRingingCalls = new LinkedHashSet<>(1); 76 mHoldingCalls = new LinkedHashSet<>(1); 77 mAudioProcessingCalls = new LinkedHashSet<>(1); 78 mCalls = new HashSet<>(); 79 mCallStateToCalls = new SparseArray<LinkedHashSet<Call>>() {{ 80 put(CallState.CONNECTING, mActiveDialingOrConnectingCalls); 81 put(CallState.ACTIVE, mActiveDialingOrConnectingCalls); 82 put(CallState.DIALING, mActiveDialingOrConnectingCalls); 83 put(CallState.PULLING, mActiveDialingOrConnectingCalls); 84 put(CallState.RINGING, mRingingCalls); 85 put(CallState.ON_HOLD, mHoldingCalls); 86 put(CallState.SIMULATED_RINGING, mRingingCalls); 87 put(CallState.AUDIO_PROCESSING, mAudioProcessingCalls); 88 }}; 89 90 mCallAudioRouteStateMachine = callAudioRouteStateMachine; 91 mCallAudioModeStateMachine = callAudioModeStateMachine; 92 mCallsManager = callsManager; 93 mPlayerFactory = playerFactory; 94 mRinger = ringer; 95 mRingbackPlayer = ringbackPlayer; 96 mBluetoothStateReceiver = bluetoothStateReceiver; 97 mDtmfLocalTonePlayer = dtmfLocalTonePlayer; 98 99 mPlayerFactory.setCallAudioManager(this); 100 mCallAudioModeStateMachine.setCallAudioManager(this); 101 mCallAudioRouteStateMachine.setCallAudioManager(this); 102 } 103 104 @Override onCallStateChanged(Call call, int oldState, int newState)105 public void onCallStateChanged(Call call, int oldState, int newState) { 106 if (shouldIgnoreCallForAudio(call)) { 107 // No audio management for calls in a conference, or external calls. 108 return; 109 } 110 Log.d(LOG_TAG, "Call state changed for TC@%s: %s -> %s", call.getId(), 111 CallState.toString(oldState), CallState.toString(newState)); 112 113 removeCallFromAllBins(call); 114 HashSet<Call> newBinForCall = getBinForCall(call); 115 if (newBinForCall != null) { 116 newBinForCall.add(call); 117 } 118 sendCallStatusToBluetoothStateReceiver(); 119 120 updateForegroundCall(); 121 if (shouldPlayDisconnectTone(oldState, newState)) { 122 playToneForDisconnectedCall(call); 123 } 124 125 onCallLeavingState(call, oldState); 126 onCallEnteringState(call, newState); 127 } 128 129 @Override onCallAdded(Call call)130 public void onCallAdded(Call call) { 131 if (shouldIgnoreCallForAudio(call)) { 132 return; // Don't do audio handling for calls in a conference, or external calls. 133 } 134 135 addCall(call); 136 } 137 138 @Override onCallRemoved(Call call)139 public void onCallRemoved(Call call) { 140 if (shouldIgnoreCallForAudio(call)) { 141 return; // Don't do audio handling for calls in a conference, or external calls. 142 } 143 144 removeCall(call); 145 } 146 addCall(Call call)147 private void addCall(Call call) { 148 if (mCalls.contains(call)) { 149 Log.w(LOG_TAG, "Call TC@%s is being added twice.", call.getId()); 150 return; // No guarantees that the same call won't get added twice. 151 } 152 153 Log.d(LOG_TAG, "Call added with id TC@%s in state %s", call.getId(), 154 CallState.toString(call.getState())); 155 156 HashSet<Call> newBinForCall = getBinForCall(call); 157 if (newBinForCall != null) { 158 newBinForCall.add(call); 159 } 160 updateForegroundCall(); 161 mCalls.add(call); 162 sendCallStatusToBluetoothStateReceiver(); 163 164 onCallEnteringState(call, call.getState()); 165 } 166 removeCall(Call call)167 private void removeCall(Call call) { 168 if (!mCalls.contains(call)) { 169 return; // No guarantees that the same call won't get removed twice. 170 } 171 172 Log.d(LOG_TAG, "Call removed with id TC@%s in state %s", call.getId(), 173 CallState.toString(call.getState())); 174 175 removeCallFromAllBins(call); 176 177 updateForegroundCall(); 178 mCalls.remove(call); 179 sendCallStatusToBluetoothStateReceiver(); 180 181 onCallLeavingState(call, call.getState()); 182 } 183 sendCallStatusToBluetoothStateReceiver()184 private void sendCallStatusToBluetoothStateReceiver() { 185 // We're in a call if there are calls in mCalls that are not in mAudioProcessingCalls. 186 boolean isInCall = !mAudioProcessingCalls.containsAll(mCalls); 187 mBluetoothStateReceiver.setIsInCall(isInCall); 188 } 189 190 /** 191 * Handles changes to the external state of a call. External calls which become regular calls 192 * should be tracked, and regular calls which become external should no longer be tracked. 193 * 194 * @param call The call. 195 * @param isExternalCall {@code True} if the call is now external, {@code false} if it is now 196 * a regular call. 197 */ 198 @Override onExternalCallChanged(Call call, boolean isExternalCall)199 public void onExternalCallChanged(Call call, boolean isExternalCall) { 200 if (isExternalCall) { 201 Log.d(LOG_TAG, "Removing call which became external ID %s", call.getId()); 202 removeCall(call); 203 } else if (!isExternalCall) { 204 Log.d(LOG_TAG, "Adding external call which was pulled with ID %s", call.getId()); 205 addCall(call); 206 207 if (mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(call.getVideoState())) { 208 // When pulling a video call, automatically enable the speakerphone. 209 Log.d(LOG_TAG, "Switching to speaker because external video call %s was pulled." + 210 call.getId()); 211 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 212 CallAudioRouteStateMachine.SWITCH_SPEAKER); 213 } 214 } 215 } 216 217 /** 218 * Determines if {@link CallAudioManager} should do any audio routing operations for a call. 219 * We ignore child calls of a conference and external calls for audio routing purposes. 220 * 221 * @param call The call to check. 222 * @return {@code true} if the call should be ignored for audio routing, {@code false} 223 * otherwise 224 */ shouldIgnoreCallForAudio(Call call)225 private boolean shouldIgnoreCallForAudio(Call call) { 226 return call.getParentCall() != null || call.isExternalCall(); 227 } 228 229 @Override onIncomingCallAnswered(Call call)230 public void onIncomingCallAnswered(Call call) { 231 if (!mCalls.contains(call)) { 232 return; 233 } 234 235 // Turn off mute when a new incoming call is answered iff it's not a handover. 236 if (!call.isHandoverInProgress()) { 237 mute(false /* shouldMute */); 238 } 239 240 maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call); 241 } 242 243 @Override onSessionModifyRequestReceived(Call call, VideoProfile videoProfile)244 public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) { 245 if (videoProfile == null) { 246 return; 247 } 248 249 if (call != mForegroundCall) { 250 // We only play tones for foreground calls. 251 return; 252 } 253 254 int previousVideoState = call.getVideoState(); 255 int newVideoState = videoProfile.getVideoState(); 256 Log.v(this, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile 257 .videoStateToString(newVideoState)); 258 259 boolean isUpgradeRequest = !VideoProfile.isReceptionEnabled(previousVideoState) && 260 VideoProfile.isReceptionEnabled(newVideoState); 261 262 if (isUpgradeRequest) { 263 mPlayerFactory.createPlayer(InCallTonePlayer.TONE_VIDEO_UPGRADE).startTone(); 264 } 265 } 266 267 /** 268 * Play or stop a call hold tone for a call. Triggered via 269 * {@link Connection#sendConnectionEvent(String)} when the 270 * {@link Connection#EVENT_ON_HOLD_TONE_START} event or 271 * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the 272 * 273 * @param call The call which requested the hold tone. 274 */ 275 @Override onHoldToneRequested(Call call)276 public void onHoldToneRequested(Call call) { 277 maybePlayHoldTone(); 278 } 279 280 @Override onIsVoipAudioModeChanged(Call call)281 public void onIsVoipAudioModeChanged(Call call) { 282 if (call != mForegroundCall) { 283 return; 284 } 285 mCallAudioModeStateMachine.sendMessageWithArgs( 286 CallAudioModeStateMachine.FOREGROUND_VOIP_MODE_CHANGE, 287 makeArgsForModeStateMachine()); 288 } 289 290 @Override onRingbackRequested(Call call, boolean shouldRingback)291 public void onRingbackRequested(Call call, boolean shouldRingback) { 292 if (call == mForegroundCall && shouldRingback) { 293 mRingbackPlayer.startRingbackForCall(call); 294 } else { 295 mRingbackPlayer.stopRingbackForCall(call); 296 } 297 } 298 299 @Override onIncomingCallRejected(Call call, boolean rejectWithMessage, String message)300 public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String message) { 301 maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(call); 302 } 303 304 @Override onIsConferencedChanged(Call call)305 public void onIsConferencedChanged(Call call) { 306 // This indicates a conferencing change, which shouldn't impact any audio mode stuff. 307 Call parentCall = call.getParentCall(); 308 if (parentCall == null) { 309 // Indicates that the call should be tracked for audio purposes. Treat it as if it were 310 // just added. 311 Log.i(LOG_TAG, "Call TC@" + call.getId() + " left conference and will" + 312 " now be tracked by CallAudioManager."); 313 onCallAdded(call); 314 } else { 315 // The call joined a conference, so stop tracking it. 316 removeCallFromAllBins(call); 317 updateForegroundCall(); 318 mCalls.remove(call); 319 } 320 } 321 322 @Override onConnectionServiceChanged(Call call, ConnectionServiceWrapper oldCs, ConnectionServiceWrapper newCs)323 public void onConnectionServiceChanged(Call call, ConnectionServiceWrapper oldCs, 324 ConnectionServiceWrapper newCs) { 325 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 326 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); 327 } 328 329 @Override onVideoStateChanged(Call call, int previousVideoState, int newVideoState)330 public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) { 331 if (call != getForegroundCall()) { 332 Log.d(LOG_TAG, "Ignoring video state change from %s to %s for call %s -- not " + 333 "foreground.", VideoProfile.videoStateToString(previousVideoState), 334 VideoProfile.videoStateToString(newVideoState), call.getId()); 335 return; 336 } 337 338 if (!VideoProfile.isVideo(previousVideoState) && 339 mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(newVideoState)) { 340 Log.d(LOG_TAG, "Switching to speaker because call %s transitioned video state from %s" + 341 " to %s", call.getId(), VideoProfile.videoStateToString(previousVideoState), 342 VideoProfile.videoStateToString(newVideoState)); 343 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 344 CallAudioRouteStateMachine.SWITCH_SPEAKER); 345 } 346 } 347 getCallAudioState()348 public CallAudioState getCallAudioState() { 349 return mCallAudioRouteStateMachine.getCurrentCallAudioState(); 350 } 351 getPossiblyHeldForegroundCall()352 public Call getPossiblyHeldForegroundCall() { 353 return mForegroundCall; 354 } 355 getForegroundCall()356 public Call getForegroundCall() { 357 if (mForegroundCall != null && mForegroundCall.getState() != CallState.ON_HOLD) { 358 return mForegroundCall; 359 } 360 return null; 361 } 362 363 @VisibleForTesting toggleMute()364 public void toggleMute() { 365 // Don't mute if there are any emergency calls. 366 if (mCallsManager.isInEmergencyCall()) { 367 Log.v(this, "ignoring toggleMute for emergency call"); 368 return; 369 } 370 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 371 CallAudioRouteStateMachine.TOGGLE_MUTE); 372 } 373 374 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) onRingerModeChange()375 public void onRingerModeChange() { 376 mCallAudioModeStateMachine.sendMessageWithArgs( 377 CallAudioModeStateMachine.RINGER_MODE_CHANGE, makeArgsForModeStateMachine()); 378 } 379 380 @VisibleForTesting mute(boolean shouldMute)381 public void mute(boolean shouldMute) { 382 Log.v(this, "mute, shouldMute: %b", shouldMute); 383 384 // Don't mute if there are any emergency calls. 385 if (mCallsManager.isInEmergencyCall()) { 386 shouldMute = false; 387 Log.v(this, "ignoring mute for emergency call"); 388 } 389 390 mCallAudioRouteStateMachine.sendMessageWithSessionInfo(shouldMute 391 ? CallAudioRouteStateMachine.MUTE_ON : CallAudioRouteStateMachine.MUTE_OFF); 392 } 393 394 /** 395 * Changed the audio route, for example from earpiece to speaker phone. 396 * 397 * @param route The new audio route to use. See {@link CallAudioState}. 398 * @param bluetoothAddress the address of the desired bluetooth device, if route is 399 * {@link CallAudioState#ROUTE_BLUETOOTH}. 400 */ setAudioRoute(int route, String bluetoothAddress)401 void setAudioRoute(int route, String bluetoothAddress) { 402 Log.v(this, "setAudioRoute, route: %s", CallAudioState.audioRouteToString(route)); 403 switch (route) { 404 case CallAudioState.ROUTE_BLUETOOTH: 405 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 406 CallAudioRouteStateMachine.USER_SWITCH_BLUETOOTH, 0, bluetoothAddress); 407 return; 408 case CallAudioState.ROUTE_SPEAKER: 409 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 410 CallAudioRouteStateMachine.USER_SWITCH_SPEAKER); 411 return; 412 case CallAudioState.ROUTE_WIRED_HEADSET: 413 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 414 CallAudioRouteStateMachine.USER_SWITCH_HEADSET); 415 return; 416 case CallAudioState.ROUTE_EARPIECE: 417 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 418 CallAudioRouteStateMachine.USER_SWITCH_EARPIECE); 419 return; 420 case CallAudioState.ROUTE_WIRED_OR_EARPIECE: 421 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 422 CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE, 423 CallAudioRouteStateMachine.NO_INCLUDE_BLUETOOTH_IN_BASELINE); 424 return; 425 default: 426 Log.wtf(this, "Invalid route specified: %d", route); 427 } 428 } 429 430 /** 431 * Switch call audio routing to the baseline route, including bluetooth headsets if there are 432 * any connected. 433 */ switchBaseline()434 void switchBaseline() { 435 Log.i(this, "switchBaseline"); 436 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 437 CallAudioRouteStateMachine.USER_SWITCH_BASELINE_ROUTE, 438 CallAudioRouteStateMachine.INCLUDE_BLUETOOTH_IN_BASELINE); 439 } 440 silenceRingers()441 void silenceRingers() { 442 synchronized (mCallsManager.getLock()) { 443 for (Call call : mRingingCalls) { 444 call.silence(); 445 } 446 447 mRinger.stopRinging(); 448 mRinger.stopCallWaiting(); 449 } 450 } 451 452 @VisibleForTesting startRinging()453 public boolean startRinging() { 454 synchronized (mCallsManager.getLock()) { 455 return mRinger.startRinging(mForegroundCall, 456 mCallAudioRouteStateMachine.isHfpDeviceAvailable()); 457 } 458 } 459 460 @VisibleForTesting startCallWaiting(String reason)461 public void startCallWaiting(String reason) { 462 synchronized (mCallsManager.getLock()) { 463 if (mRingingCalls.size() == 1) { 464 mRinger.startCallWaiting(mRingingCalls.iterator().next(), reason); 465 } 466 } 467 } 468 469 @VisibleForTesting stopRinging()470 public void stopRinging() { 471 synchronized (mCallsManager.getLock()) { 472 mRinger.stopRinging(); 473 } 474 } 475 476 @VisibleForTesting stopCallWaiting()477 public void stopCallWaiting() { 478 synchronized (mCallsManager.getLock()) { 479 mRinger.stopCallWaiting(); 480 } 481 } 482 483 @VisibleForTesting setCallAudioRouteFocusState(int focusState)484 public void setCallAudioRouteFocusState(int focusState) { 485 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 486 CallAudioRouteStateMachine.SWITCH_FOCUS, focusState); 487 } 488 notifyAudioOperationsComplete()489 public void notifyAudioOperationsComplete() { 490 mCallAudioModeStateMachine.sendMessageWithArgs( 491 CallAudioModeStateMachine.AUDIO_OPERATIONS_COMPLETE, makeArgsForModeStateMachine()); 492 } 493 494 @VisibleForTesting getCallAudioRouteStateMachine()495 public CallAudioRouteStateMachine getCallAudioRouteStateMachine() { 496 return mCallAudioRouteStateMachine; 497 } 498 499 @VisibleForTesting getCallAudioModeStateMachine()500 public CallAudioModeStateMachine getCallAudioModeStateMachine() { 501 return mCallAudioModeStateMachine; 502 } 503 dump(IndentingPrintWriter pw)504 void dump(IndentingPrintWriter pw) { 505 pw.println("All calls:"); 506 pw.increaseIndent(); 507 dumpCallsInCollection(pw, mCalls); 508 pw.decreaseIndent(); 509 510 pw.println("Active dialing, or connecting calls:"); 511 pw.increaseIndent(); 512 dumpCallsInCollection(pw, mActiveDialingOrConnectingCalls); 513 pw.decreaseIndent(); 514 515 pw.println("Ringing calls:"); 516 pw.increaseIndent(); 517 dumpCallsInCollection(pw, mRingingCalls); 518 pw.decreaseIndent(); 519 520 pw.println("Holding calls:"); 521 pw.increaseIndent(); 522 dumpCallsInCollection(pw, mHoldingCalls); 523 pw.decreaseIndent(); 524 525 pw.println("Foreground call:"); 526 pw.println(mForegroundCall); 527 528 pw.println("CallAudioModeStateMachine pending messages:"); 529 pw.increaseIndent(); 530 mCallAudioModeStateMachine.dumpPendingMessages(pw); 531 pw.decreaseIndent(); 532 533 pw.println("CallAudioRouteStateMachine pending messages:"); 534 pw.increaseIndent(); 535 mCallAudioRouteStateMachine.dumpPendingMessages(pw); 536 pw.decreaseIndent(); 537 538 pw.println("BluetoothDeviceManager:"); 539 pw.increaseIndent(); 540 if (mBluetoothStateReceiver.getBluetoothDeviceManager() != null) { 541 mBluetoothStateReceiver.getBluetoothDeviceManager().dump(pw); 542 } 543 pw.decreaseIndent(); 544 } 545 546 @VisibleForTesting setIsTonePlaying(boolean isTonePlaying)547 public void setIsTonePlaying(boolean isTonePlaying) { 548 mIsTonePlaying = isTonePlaying; 549 mCallAudioModeStateMachine.sendMessageWithArgs( 550 isTonePlaying ? CallAudioModeStateMachine.TONE_STARTED_PLAYING 551 : CallAudioModeStateMachine.TONE_STOPPED_PLAYING, 552 makeArgsForModeStateMachine()); 553 554 if (!isTonePlaying && mIsDisconnectedTonePlaying) { 555 mCallsManager.onDisconnectedTonePlaying(false); 556 mIsDisconnectedTonePlaying = false; 557 } 558 } 559 onCallLeavingState(Call call, int state)560 private void onCallLeavingState(Call call, int state) { 561 switch (state) { 562 case CallState.ACTIVE: 563 case CallState.CONNECTING: 564 onCallLeavingActiveDialingOrConnecting(); 565 break; 566 case CallState.RINGING: 567 case CallState.SIMULATED_RINGING: 568 case CallState.ANSWERED: 569 onCallLeavingRinging(); 570 break; 571 case CallState.ON_HOLD: 572 onCallLeavingHold(); 573 break; 574 case CallState.PULLING: 575 onCallLeavingActiveDialingOrConnecting(); 576 break; 577 case CallState.DIALING: 578 stopRingbackForCall(call); 579 onCallLeavingActiveDialingOrConnecting(); 580 break; 581 case CallState.AUDIO_PROCESSING: 582 onCallLeavingAudioProcessing(); 583 break; 584 } 585 } 586 onCallEnteringState(Call call, int state)587 private void onCallEnteringState(Call call, int state) { 588 switch (state) { 589 case CallState.ACTIVE: 590 case CallState.CONNECTING: 591 onCallEnteringActiveDialingOrConnecting(); 592 break; 593 case CallState.RINGING: 594 case CallState.SIMULATED_RINGING: 595 onCallEnteringRinging(); 596 break; 597 case CallState.ON_HOLD: 598 onCallEnteringHold(); 599 break; 600 case CallState.PULLING: 601 onCallEnteringActiveDialingOrConnecting(); 602 break; 603 case CallState.DIALING: 604 onCallEnteringActiveDialingOrConnecting(); 605 playRingbackForCall(call); 606 break; 607 case CallState.ANSWERED: 608 if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) { 609 onCallEnteringActiveDialingOrConnecting(); 610 } 611 break; 612 case CallState.AUDIO_PROCESSING: 613 onCallEnteringAudioProcessing(); 614 break; 615 } 616 } 617 onCallLeavingAudioProcessing()618 private void onCallLeavingAudioProcessing() { 619 if (mAudioProcessingCalls.size() == 0) { 620 mCallAudioModeStateMachine.sendMessageWithArgs( 621 CallAudioModeStateMachine.NO_MORE_AUDIO_PROCESSING_CALLS, 622 makeArgsForModeStateMachine()); 623 } 624 } 625 onCallEnteringAudioProcessing()626 private void onCallEnteringAudioProcessing() { 627 if (mAudioProcessingCalls.size() == 1) { 628 mCallAudioModeStateMachine.sendMessageWithArgs( 629 CallAudioModeStateMachine.NEW_AUDIO_PROCESSING_CALL, 630 makeArgsForModeStateMachine()); 631 } 632 } 633 onCallLeavingActiveDialingOrConnecting()634 private void onCallLeavingActiveDialingOrConnecting() { 635 if (mActiveDialingOrConnectingCalls.size() == 0) { 636 mCallAudioModeStateMachine.sendMessageWithArgs( 637 CallAudioModeStateMachine.NO_MORE_ACTIVE_OR_DIALING_CALLS, 638 makeArgsForModeStateMachine()); 639 } 640 } 641 onCallLeavingRinging()642 private void onCallLeavingRinging() { 643 if (mRingingCalls.size() == 0) { 644 mCallAudioModeStateMachine.sendMessageWithArgs( 645 CallAudioModeStateMachine.NO_MORE_RINGING_CALLS, 646 makeArgsForModeStateMachine()); 647 } 648 } 649 onCallLeavingHold()650 private void onCallLeavingHold() { 651 if (mHoldingCalls.size() == 0) { 652 mCallAudioModeStateMachine.sendMessageWithArgs( 653 CallAudioModeStateMachine.NO_MORE_HOLDING_CALLS, 654 makeArgsForModeStateMachine()); 655 } 656 } 657 onCallEnteringActiveDialingOrConnecting()658 private void onCallEnteringActiveDialingOrConnecting() { 659 if (mActiveDialingOrConnectingCalls.size() == 1) { 660 mCallAudioModeStateMachine.sendMessageWithArgs( 661 CallAudioModeStateMachine.NEW_ACTIVE_OR_DIALING_CALL, 662 makeArgsForModeStateMachine()); 663 } 664 } 665 onCallEnteringRinging()666 private void onCallEnteringRinging() { 667 if (mRingingCalls.size() == 1) { 668 mCallAudioModeStateMachine.sendMessageWithArgs( 669 CallAudioModeStateMachine.NEW_RINGING_CALL, 670 makeArgsForModeStateMachine()); 671 } 672 } 673 onCallEnteringHold()674 private void onCallEnteringHold() { 675 if (mHoldingCalls.size() == 1) { 676 mCallAudioModeStateMachine.sendMessageWithArgs( 677 CallAudioModeStateMachine.NEW_HOLDING_CALL, 678 makeArgsForModeStateMachine()); 679 } 680 } 681 updateForegroundCall()682 private void updateForegroundCall() { 683 Call oldForegroundCall = mForegroundCall; 684 if (mActiveDialingOrConnectingCalls.size() > 0) { 685 // Give preference for connecting calls over active/dialing for foreground-ness. 686 Call possibleConnectingCall = null; 687 for (Call call : mActiveDialingOrConnectingCalls) { 688 if (call.getState() == CallState.CONNECTING) { 689 possibleConnectingCall = call; 690 } 691 } 692 mForegroundCall = possibleConnectingCall == null ? 693 mActiveDialingOrConnectingCalls.iterator().next() : possibleConnectingCall; 694 } else if (mRingingCalls.size() > 0) { 695 mForegroundCall = mRingingCalls.iterator().next(); 696 } else if (mHoldingCalls.size() > 0) { 697 mForegroundCall = mHoldingCalls.iterator().next(); 698 } else { 699 mForegroundCall = null; 700 } 701 702 if (mForegroundCall != oldForegroundCall) { 703 mCallAudioRouteStateMachine.sendMessageWithSessionInfo( 704 CallAudioRouteStateMachine.UPDATE_SYSTEM_AUDIO_ROUTE); 705 mDtmfLocalTonePlayer.onForegroundCallChanged(oldForegroundCall, mForegroundCall); 706 maybePlayHoldTone(); 707 } 708 } 709 710 @NonNull makeArgsForModeStateMachine()711 private CallAudioModeStateMachine.MessageArgs makeArgsForModeStateMachine() { 712 return new Builder() 713 .setHasActiveOrDialingCalls(mActiveDialingOrConnectingCalls.size() > 0) 714 .setHasRingingCalls(mRingingCalls.size() > 0) 715 .setHasHoldingCalls(mHoldingCalls.size() > 0) 716 .setHasAudioProcessingCalls(mAudioProcessingCalls.size() > 0) 717 .setIsTonePlaying(mIsTonePlaying) 718 .setForegroundCallIsVoip( 719 mForegroundCall != null && mForegroundCall.getIsVoipAudioMode()) 720 .setSession(Log.createSubsession()).build(); 721 } 722 getBinForCall(Call call)723 private HashSet<Call> getBinForCall(Call call) { 724 if (call.getState() == CallState.ANSWERED) { 725 // If the call has the speed-up-mt-audio capability, treat answered state as active 726 // for audio purposes. 727 if (call.can(android.telecom.Call.Details.CAPABILITY_SPEED_UP_MT_AUDIO)) { 728 return mActiveDialingOrConnectingCalls; 729 } 730 return mRingingCalls; 731 } 732 return mCallStateToCalls.get(call.getState()); 733 } 734 removeCallFromAllBins(Call call)735 private void removeCallFromAllBins(Call call) { 736 for (int i = 0; i < mCallStateToCalls.size(); i++) { 737 mCallStateToCalls.valueAt(i).remove(call); 738 } 739 } 740 playToneForDisconnectedCall(Call call)741 private void playToneForDisconnectedCall(Call call) { 742 // If this call is being disconnected as a result of being handed over to another call, 743 // we will not play a disconnect tone. 744 if (call.isHandoverInProgress()) { 745 Log.i(LOG_TAG, "Omitting tone because %s is being handed over.", call); 746 return; 747 } 748 749 if (mForegroundCall != null && call != mForegroundCall && mCalls.size() > 1) { 750 Log.v(LOG_TAG, "Omitting tone because we are not foreground" + 751 " and there is another call."); 752 return; 753 } 754 755 if (call.getDisconnectCause() != null) { 756 int toneToPlay = InCallTonePlayer.TONE_INVALID; 757 758 Log.v(this, "Disconnect cause: %s.", call.getDisconnectCause()); 759 760 switch(call.getDisconnectCause().getTone()) { 761 case ToneGenerator.TONE_SUP_BUSY: 762 toneToPlay = InCallTonePlayer.TONE_BUSY; 763 break; 764 case ToneGenerator.TONE_SUP_CONGESTION: 765 toneToPlay = InCallTonePlayer.TONE_CONGESTION; 766 break; 767 case ToneGenerator.TONE_CDMA_REORDER: 768 toneToPlay = InCallTonePlayer.TONE_REORDER; 769 break; 770 case ToneGenerator.TONE_CDMA_ABBR_INTERCEPT: 771 toneToPlay = InCallTonePlayer.TONE_INTERCEPT; 772 break; 773 case ToneGenerator.TONE_CDMA_CALLDROP_LITE: 774 toneToPlay = InCallTonePlayer.TONE_CDMA_DROP; 775 break; 776 case ToneGenerator.TONE_SUP_ERROR: 777 toneToPlay = InCallTonePlayer.TONE_UNOBTAINABLE_NUMBER; 778 break; 779 case ToneGenerator.TONE_PROP_PROMPT: 780 toneToPlay = InCallTonePlayer.TONE_CALL_ENDED; 781 break; 782 } 783 784 Log.d(this, "Found a disconnected call with tone to play %d.", toneToPlay); 785 786 if (toneToPlay != InCallTonePlayer.TONE_INVALID) { 787 boolean didToneStart = mPlayerFactory.createPlayer(toneToPlay).startTone(); 788 if (didToneStart) { 789 mCallsManager.onDisconnectedTonePlaying(true); 790 mIsDisconnectedTonePlaying = true; 791 } 792 } 793 } 794 } 795 playRingbackForCall(Call call)796 private void playRingbackForCall(Call call) { 797 if (call == mForegroundCall && call.isRingbackRequested()) { 798 mRingbackPlayer.startRingbackForCall(call); 799 } 800 } 801 stopRingbackForCall(Call call)802 private void stopRingbackForCall(Call call) { 803 mRingbackPlayer.stopRingbackForCall(call); 804 } 805 806 /** 807 * Determines if a hold tone should be played and then starts or stops it accordingly. 808 */ maybePlayHoldTone()809 private void maybePlayHoldTone() { 810 if (shouldPlayHoldTone()) { 811 if (mHoldTonePlayer == null) { 812 mHoldTonePlayer = mPlayerFactory.createPlayer(InCallTonePlayer.TONE_CALL_WAITING); 813 mHoldTonePlayer.startTone(); 814 } 815 } else { 816 if (mHoldTonePlayer != null) { 817 mHoldTonePlayer.stopTone(); 818 mHoldTonePlayer = null; 819 } 820 } 821 } 822 823 /** 824 * Determines if a hold tone should be played. 825 * A hold tone should be played only if foreground call is equals with call which is 826 * remotely held. 827 * 828 * @return {@code true} if the the hold tone should be played, {@code false} otherwise. 829 */ shouldPlayHoldTone()830 private boolean shouldPlayHoldTone() { 831 Call foregroundCall = getForegroundCall(); 832 // If there is no foreground call, no hold tone should play. 833 if (foregroundCall == null) { 834 return false; 835 } 836 837 // If another call is ringing, no hold tone should play. 838 if (mCallsManager.hasRingingCall()) { 839 return false; 840 } 841 842 // If the foreground call isn't active, no hold tone should play. This might happen, for 843 // example, if the user puts a remotely held call on hold itself. 844 if (!foregroundCall.isActive()) { 845 return false; 846 } 847 848 return foregroundCall.isRemotelyHeld(); 849 } 850 dumpCallsInCollection(IndentingPrintWriter pw, Collection<Call> calls)851 private void dumpCallsInCollection(IndentingPrintWriter pw, Collection<Call> calls) { 852 for (Call call : calls) { 853 if (call != null) pw.println(call.getId()); 854 } 855 } 856 maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(Call call)857 private void maybeStopRingingAndCallWaitingForAnsweredOrRejectedCall(Call call) { 858 // Check to see if the call being answered/rejected is the only ringing call, since this 859 // will be called before the connection service acknowledges the state change. 860 synchronized (mCallsManager.getLock()) { 861 if (mRingingCalls.size() == 0 || 862 (mRingingCalls.size() == 1 && call == mRingingCalls.iterator().next())) { 863 mRinger.stopRinging(); 864 mRinger.stopCallWaiting(); 865 } 866 } 867 } 868 shouldPlayDisconnectTone(int oldState, int newState)869 private boolean shouldPlayDisconnectTone(int oldState, int newState) { 870 if (newState != CallState.DISCONNECTED) { 871 return false; 872 } 873 return oldState == CallState.ACTIVE || 874 oldState == CallState.DIALING || 875 oldState == CallState.ON_HOLD; 876 } 877 878 @VisibleForTesting getTrackedCalls()879 public Set<Call> getTrackedCalls() { 880 return mCalls; 881 } 882 883 @VisibleForTesting getCallStateToCalls()884 public SparseArray<LinkedHashSet<Call>> getCallStateToCalls() { 885 return mCallStateToCalls; 886 } 887 } 888