CallsManager.java revision 8de76915ea2772faeb41705aaaeb65f5b3478ac4
1/* 2 * Copyright (C) 2013 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 17package com.android.server.telecom; 18 19import android.content.Context; 20import android.net.Uri; 21import android.os.Bundle; 22import android.os.Handler; 23import android.os.Trace; 24import android.provider.CallLog.Calls; 25import android.telecom.AudioState; 26import android.telecom.CallState; 27import android.telecom.Conference; 28import android.telecom.Connection; 29import android.telecom.DisconnectCause; 30import android.telecom.GatewayInfo; 31import android.telecom.ParcelableConference; 32import android.telecom.ParcelableConnection; 33import android.telecom.PhoneAccount; 34import android.telecom.PhoneAccountHandle; 35import android.telecom.TelecomManager; 36import android.telecom.VideoProfile; 37import android.telephony.PhoneNumberUtils; 38import android.telephony.TelephonyManager; 39 40import com.android.internal.annotations.VisibleForTesting; 41import com.android.internal.util.IndentingPrintWriter; 42 43import java.util.Collection; 44import java.util.Collections; 45import java.util.HashSet; 46import java.util.List; 47import java.util.Objects; 48import java.util.Set; 49import java.util.concurrent.ConcurrentHashMap; 50 51/** 52 * Singleton. 53 * 54 * NOTE: by design most APIs are package private, use the relevant adapter/s to allow 55 * access from other packages specifically refraining from passing the CallsManager instance 56 * beyond the com.android.server.telecom package boundary. 57 */ 58public final class CallsManager extends Call.ListenerBase { 59 60 // TODO: Consider renaming this CallsManagerPlugin. 61 interface CallsManagerListener { 62 void onCallAdded(Call call); 63 void onCallRemoved(Call call); 64 void onCallStateChanged(Call call, int oldState, int newState); 65 void onConnectionServiceChanged( 66 Call call, 67 ConnectionServiceWrapper oldService, 68 ConnectionServiceWrapper newService); 69 void onIncomingCallAnswered(Call call); 70 void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage); 71 void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall); 72 void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState); 73 void onRingbackRequested(Call call, boolean ringback); 74 void onIsConferencedChanged(Call call); 75 void onIsVoipAudioModeChanged(Call call); 76 void onVideoStateChanged(Call call); 77 void onCanAddCallChanged(boolean canAddCall); 78 } 79 80 private static final String TAG = "CallsManager"; 81 82 private static final int MAXIMUM_LIVE_CALLS = 1; 83 private static final int MAXIMUM_HOLD_CALLS = 1; 84 private static final int MAXIMUM_RINGING_CALLS = 1; 85 private static final int MAXIMUM_OUTGOING_CALLS = 1; 86 private static final int MAXIMUM_TOP_LEVEL_CALLS = 2; 87 88 private static final int[] OUTGOING_CALL_STATES = 89 {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING}; 90 91 private static final int[] LIVE_CALL_STATES = 92 {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING, CallState.ACTIVE}; 93 94 /** 95 * The main call repository. Keeps an instance of all live calls. New incoming and outgoing 96 * calls are added to the map and removed when the calls move to the disconnected state. 97 * 98 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is 99 * load factor before resizing, 1 means we only expect a single thread to 100 * access the map so make only a single shard 101 */ 102 private final Set<Call> mCalls = Collections.newSetFromMap( 103 new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1)); 104 105 private final ConnectionServiceRepository mConnectionServiceRepository; 106 private final DtmfLocalTonePlayer mDtmfLocalTonePlayer; 107 private final InCallController mInCallController; 108 private final CallAudioManager mCallAudioManager; 109 private RespondViaSmsManager mRespondViaSmsManager; 110 private final Ringer mRinger; 111 private final InCallWakeLockController mInCallWakeLockController; 112 // For this set initial table size to 16 because we add 13 listeners in 113 // the CallsManager constructor. 114 private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap( 115 new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1)); 116 private final HeadsetMediaButton mHeadsetMediaButton; 117 private final WiredHeadsetManager mWiredHeadsetManager; 118 private final TtyManager mTtyManager; 119 private final ProximitySensorManager mProximitySensorManager; 120 private final PhoneStateBroadcaster mPhoneStateBroadcaster; 121 private final CallLogManager mCallLogManager; 122 private final Context mContext; 123 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 124 private final MissedCallNotifier mMissedCallNotifier; 125 private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>(); 126 private final Set<Call> mPendingCallsToDisconnect = new HashSet<>(); 127 /* Handler tied to thread in which CallManager was initialized. */ 128 private final Handler mHandler = new Handler(); 129 130 private boolean mCanAddCall = true; 131 132 /** 133 * The call the user is currently interacting with. This is the call that should have audio 134 * focus and be visible in the in-call UI. 135 */ 136 private Call mForegroundCall; 137 138 private Runnable mStopTone; 139 140 /** 141 * Initializes the required Telecom components. 142 */ 143 CallsManager( 144 Context context, 145 MissedCallNotifier missedCallNotifier, 146 PhoneAccountRegistrar phoneAccountRegistrar, 147 HeadsetMediaButtonFactory headsetMediaButtonFactory, 148 ProximitySensorManagerFactory proximitySensorManagerFactory, 149 InCallWakeLockControllerFactory inCallWakeLockControllerFactory) { 150 mContext = context; 151 mPhoneAccountRegistrar = phoneAccountRegistrar; 152 mMissedCallNotifier = missedCallNotifier; 153 StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this); 154 mWiredHeadsetManager = new WiredHeadsetManager(context); 155 mCallAudioManager = new CallAudioManager( 156 context, statusBarNotifier, mWiredHeadsetManager, this); 157 InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager); 158 mRinger = new Ringer(mCallAudioManager, this, playerFactory, context); 159 mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this); 160 mTtyManager = new TtyManager(context, mWiredHeadsetManager); 161 mProximitySensorManager = proximitySensorManagerFactory.create(context, this); 162 mPhoneStateBroadcaster = new PhoneStateBroadcaster(this); 163 mCallLogManager = new CallLogManager(context); 164 mInCallController = new InCallController(context, this); 165 mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context); 166 mConnectionServiceRepository = 167 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, this); 168 mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this); 169 170 mListeners.add(statusBarNotifier); 171 mListeners.add(mCallLogManager); 172 mListeners.add(mPhoneStateBroadcaster); 173 mListeners.add(mInCallController); 174 mListeners.add(mRinger); 175 mListeners.add(new RingbackPlayer(this, playerFactory)); 176 mListeners.add(new InCallToneMonitor(playerFactory, this)); 177 mListeners.add(mCallAudioManager); 178 mListeners.add(missedCallNotifier); 179 mListeners.add(mDtmfLocalTonePlayer); 180 mListeners.add(mHeadsetMediaButton); 181 mListeners.add(mProximitySensorManager); 182 } 183 184 public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) { 185 if (mRespondViaSmsManager != null) { 186 mListeners.remove(mRespondViaSmsManager); 187 } 188 mRespondViaSmsManager = respondViaSmsManager; 189 mListeners.add(respondViaSmsManager); 190 } 191 192 public RespondViaSmsManager getRespondViaSmsManager() { 193 return mRespondViaSmsManager; 194 } 195 196 @Override 197 public void onSuccessfulOutgoingCall(Call call, int callState) { 198 Log.v(this, "onSuccessfulOutgoingCall, %s", call); 199 200 setCallState(call, callState); 201 if (!mCalls.contains(call)) { 202 // Call was not added previously in startOutgoingCall due to it being a potential MMI 203 // code, so add it now. 204 addCall(call); 205 } 206 207 // The call's ConnectionService has been updated. 208 for (CallsManagerListener listener : mListeners) { 209 listener.onConnectionServiceChanged(call, null, call.getConnectionService()); 210 } 211 212 markCallAsDialing(call); 213 } 214 215 @Override 216 public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) { 217 Log.v(this, "onFailedOutgoingCall, call: %s", call); 218 219 markCallAsRemoved(call); 220 } 221 222 @Override 223 public void onSuccessfulIncomingCall(Call incomingCall) { 224 Log.d(this, "onSuccessfulIncomingCall"); 225 setCallState(incomingCall, CallState.RINGING); 226 227 if (hasMaximumRingingCalls()) { 228 incomingCall.reject(false, null); 229 // since the call was not added to the list of calls, we have to call the missed 230 // call notifier and the call logger manually. 231 mMissedCallNotifier.showMissedCallNotification(incomingCall); 232 mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE); 233 } else { 234 addCall(incomingCall); 235 } 236 } 237 238 @Override 239 public void onFailedIncomingCall(Call call) { 240 setCallState(call, CallState.DISCONNECTED); 241 call.removeListener(this); 242 } 243 244 @Override 245 public void onSuccessfulUnknownCall(Call call, int callState) { 246 setCallState(call, callState); 247 Log.i(this, "onSuccessfulUnknownCall for call %s", call); 248 addCall(call); 249 } 250 251 @Override 252 public void onFailedUnknownCall(Call call) { 253 Log.i(this, "onFailedUnknownCall for call %s", call); 254 setCallState(call, CallState.DISCONNECTED); 255 call.removeListener(this); 256 } 257 258 @Override 259 public void onRingbackRequested(Call call, boolean ringback) { 260 for (CallsManagerListener listener : mListeners) { 261 listener.onRingbackRequested(call, ringback); 262 } 263 } 264 265 @Override 266 public void onPostDialWait(Call call, String remaining) { 267 mInCallController.onPostDialWait(call, remaining); 268 } 269 270 @Override 271 public void onPostDialChar(final Call call, char nextChar) { 272 if (PhoneNumberUtils.is12Key(nextChar)) { 273 // Play tone if it is one of the dialpad digits, canceling out the previously queued 274 // up stopTone runnable since playing a new tone automatically stops the previous tone. 275 if (mStopTone != null) { 276 mHandler.removeCallbacks(mStopTone); 277 } 278 279 mDtmfLocalTonePlayer.playTone(call, nextChar); 280 281 mStopTone = new Runnable() { 282 @Override 283 public void run() { 284 // Set a timeout to stop the tone in case there isn't another tone to follow. 285 mDtmfLocalTonePlayer.stopTone(call); 286 } 287 }; 288 mHandler.postDelayed( 289 mStopTone, 290 Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver())); 291 } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT || 292 nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) { 293 // Stop the tone if a tone is playing, removing any other stopTone callbacks since 294 // the previous tone is being stopped anyway. 295 if (mStopTone != null) { 296 mHandler.removeCallbacks(mStopTone); 297 } 298 mDtmfLocalTonePlayer.stopTone(call); 299 } else { 300 Log.w(this, "onPostDialChar: invalid value %d", nextChar); 301 } 302 } 303 304 @Override 305 public void onParentChanged(Call call) { 306 // parent-child relationship affects which call should be foreground, so do an update. 307 updateCallsManagerState(); 308 for (CallsManagerListener listener : mListeners) { 309 listener.onIsConferencedChanged(call); 310 } 311 } 312 313 @Override 314 public void onChildrenChanged(Call call) { 315 // parent-child relationship affects which call should be foreground, so do an update. 316 updateCallsManagerState(); 317 for (CallsManagerListener listener : mListeners) { 318 listener.onIsConferencedChanged(call); 319 } 320 } 321 322 @Override 323 public void onIsVoipAudioModeChanged(Call call) { 324 for (CallsManagerListener listener : mListeners) { 325 listener.onIsVoipAudioModeChanged(call); 326 } 327 } 328 329 @Override 330 public void onVideoStateChanged(Call call) { 331 for (CallsManagerListener listener : mListeners) { 332 listener.onVideoStateChanged(call); 333 } 334 } 335 336 @Override 337 public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call) { 338 mPendingCallsToDisconnect.add(call); 339 mHandler.postDelayed(new Runnable() { 340 @Override 341 public void run() { 342 if (mPendingCallsToDisconnect.remove(call)) { 343 Log.i(this, "Delayed disconnection of call: %s", call); 344 call.disconnect(); 345 } 346 } 347 }, Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver())); 348 349 return true; 350 } 351 352 Collection<Call> getCalls() { 353 return Collections.unmodifiableCollection(mCalls); 354 } 355 356 Call getForegroundCall() { 357 return mForegroundCall; 358 } 359 360 Ringer getRinger() { 361 return mRinger; 362 } 363 364 InCallController getInCallController() { 365 return mInCallController; 366 } 367 368 boolean hasEmergencyCall() { 369 for (Call call : mCalls) { 370 if (call.isEmergencyCall()) { 371 return true; 372 } 373 } 374 return false; 375 } 376 377 boolean hasVideoCall() { 378 for (Call call : mCalls) { 379 if (call.getVideoState() != VideoProfile.VideoState.AUDIO_ONLY) { 380 return true; 381 } 382 } 383 return false; 384 } 385 386 AudioState getAudioState() { 387 return mCallAudioManager.getAudioState(); 388 } 389 390 boolean isTtySupported() { 391 return mTtyManager.isTtySupported(); 392 } 393 394 int getCurrentTtyMode() { 395 return mTtyManager.getCurrentTtyMode(); 396 } 397 398 void addListener(CallsManagerListener listener) { 399 mListeners.add(listener); 400 } 401 402 void removeListener(CallsManagerListener listener) { 403 mListeners.remove(listener); 404 } 405 406 /** 407 * Starts the process to attach the call to a connection service. 408 * 409 * @param phoneAccountHandle The phone account which contains the component name of the 410 * connection service to use for this call. 411 * @param extras The optional extras Bundle passed with the intent used for the incoming call. 412 */ 413 void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 414 Log.d(this, "processIncomingCallIntent"); 415 Uri handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER); 416 Call call = new Call( 417 mContext, 418 this, 419 mConnectionServiceRepository, 420 handle, 421 null /* gatewayInfo */, 422 null /* connectionManagerPhoneAccount */, 423 phoneAccountHandle, 424 true /* isIncoming */, 425 false /* isConference */); 426 427 call.setExtras(extras); 428 // TODO: Move this to be a part of addCall() 429 call.addListener(this); 430 call.startCreateConnection(mPhoneAccountRegistrar); 431 } 432 433 void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 434 Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE); 435 Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle)); 436 Call call = new Call( 437 mContext, 438 this, 439 mConnectionServiceRepository, 440 handle, 441 null /* gatewayInfo */, 442 null /* connectionManagerPhoneAccount */, 443 phoneAccountHandle, 444 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach 445 // to the existing connection instead of trying to create a new one. 446 true /* isIncoming */, 447 false /* isConference */); 448 call.setIsUnknown(true); 449 call.setExtras(extras); 450 call.addListener(this); 451 call.startCreateConnection(mPhoneAccountRegistrar); 452 } 453 454 private Call getNewOutgoingCall(Uri handle) { 455 // First check to see if we can reuse any of the calls that are waiting to disconnect. 456 // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information. 457 Call reusedCall = null; 458 for (Call pendingCall : mPendingCallsToDisconnect) { 459 if (reusedCall == null && Objects.equals(pendingCall.getHandle(), handle)) { 460 mPendingCallsToDisconnect.remove(pendingCall); 461 Log.i(this, "Reusing disconnected call %s", pendingCall); 462 reusedCall = pendingCall; 463 } else { 464 pendingCall.disconnect(); 465 } 466 } 467 if (reusedCall != null) { 468 return reusedCall; 469 } 470 471 // Create a call with original handle. The handle may be changed when the call is attached 472 // to a connection service, but in most cases will remain the same. 473 return new Call( 474 mContext, 475 this, 476 mConnectionServiceRepository, 477 handle, 478 null /* gatewayInfo */, 479 null /* connectionManagerPhoneAccount */, 480 null /* phoneAccountHandle */, 481 false /* isIncoming */, 482 false /* isConference */); 483 } 484 485 /** 486 * Kicks off the first steps to creating an outgoing call so that InCallUI can launch. 487 * 488 * @param handle Handle to connect the call with. 489 * @param phoneAccountHandle The phone account which contains the component name of the 490 * connection service to use for this call. 491 * @param extras The optional extras Bundle passed with the intent used for the incoming call. 492 */ 493 Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) { 494 Call call = getNewOutgoingCall(handle); 495 496 List<PhoneAccountHandle> accounts = 497 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme()); 498 499 Log.v(this, "startOutgoingCall found accounts = " + accounts); 500 501 if (mForegroundCall != null && mForegroundCall.getTargetPhoneAccount() != null) { 502 // If there is an ongoing call, use the same phone account to place this new call. 503 phoneAccountHandle = mForegroundCall.getTargetPhoneAccount(); 504 } 505 506 // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call 507 // as if a phoneAccount was not specified (does the default behavior instead). 508 // Note: We will not attempt to dial with a requested phoneAccount if it is disabled. 509 if (phoneAccountHandle != null) { 510 if (!accounts.contains(phoneAccountHandle)) { 511 phoneAccountHandle = null; 512 } 513 } 514 515 if (phoneAccountHandle == null) { 516 // No preset account, check if default exists that supports the URI scheme for the 517 // handle. 518 PhoneAccountHandle defaultAccountHandle = 519 mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount( 520 handle.getScheme()); 521 if (defaultAccountHandle != null) { 522 phoneAccountHandle = defaultAccountHandle; 523 } 524 } 525 526 call.setTargetPhoneAccount(phoneAccountHandle); 527 528 boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext, 529 call.getHandle()); 530 boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle); 531 532 // Do not support any more live calls. Our options are to move a call to hold, disconnect 533 // a call, or cancel this call altogether. 534 if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, isEmergencyCall)) { 535 // just cancel at this point. 536 Log.i(this, "No remaining room for outgoing call: %s", call); 537 if (mCalls.contains(call)) { 538 // This call can already exist if it is a reused call, 539 // See {@link #getNewOutgoingCall}. 540 call.disconnect(); 541 } 542 return null; 543 } 544 545 boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 && 546 !isEmergencyCall; 547 548 if (needsAccountSelection) { 549 // This is the state where the user is expected to select an account 550 call.setState(CallState.PRE_DIAL_WAIT); 551 extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts); 552 } else { 553 call.setState(CallState.CONNECTING); 554 } 555 556 call.setExtras(extras); 557 558 // Do not add the call if it is a potential MMI code. 559 if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) { 560 call.addListener(this); 561 } else if (!mCalls.contains(call)) { 562 // We check if mCalls already contains the call because we could potentially be reusing 563 // a call which was previously added (See {@link #getNewOutgoingCall}). 564 addCall(call); 565 } 566 567 return call; 568 } 569 570 /** 571 * Attempts to issue/connect the specified call. 572 * 573 * @param handle Handle to connect the call with. 574 * @param gatewayInfo Optional gateway information that can be used to route the call to the 575 * actual dialed handle via a gateway provider. May be null. 576 * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects. 577 * @param videoState The desired video state for the outgoing call. 578 */ 579 void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, 580 int videoState) { 581 if (call == null) { 582 // don't do anything if the call no longer exists 583 Log.i(this, "Canceling unknown call."); 584 return; 585 } 586 587 final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress(); 588 589 if (gatewayInfo == null) { 590 Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle)); 591 } else { 592 Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s", 593 Log.pii(uriHandle), Log.pii(handle)); 594 } 595 596 call.setHandle(uriHandle); 597 call.setGatewayInfo(gatewayInfo); 598 call.setStartWithSpeakerphoneOn(speakerphoneOn); 599 call.setVideoState(videoState); 600 601 boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext, 602 call.getHandle()); 603 if (isEmergencyCall) { 604 // Emergency -- CreateConnectionProcessor will choose accounts automatically 605 call.setTargetPhoneAccount(null); 606 } 607 608 if (call.getTargetPhoneAccount() != null || isEmergencyCall) { 609 // If the account has been set, proceed to place the outgoing call. 610 // Otherwise the connection will be initiated when the account is set by the user. 611 call.startCreateConnection(mPhoneAccountRegistrar); 612 } 613 } 614 615 /** 616 * Attempts to start a conference call for the specified call. 617 * 618 * @param call The call to conference. 619 * @param otherCall The other call to conference with. 620 */ 621 void conference(Call call, Call otherCall) { 622 call.conferenceWith(otherCall); 623 } 624 625 /** 626 * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call 627 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 628 * the user opting to answer said call. 629 * 630 * @param call The call to answer. 631 * @param videoState The video state in which to answer the call. 632 */ 633 void answerCall(Call call, int videoState) { 634 if (!mCalls.contains(call)) { 635 Log.i(this, "Request to answer a non-existent call %s", call); 636 } else { 637 // If the foreground call is not the ringing call and it is currently isActive() or 638 // STATE_DIALING, put it on hold before answering the call. 639 if (mForegroundCall != null && mForegroundCall != call && 640 (mForegroundCall.isActive() || 641 mForegroundCall.getState() == CallState.DIALING)) { 642 if (0 == (mForegroundCall.getConnectionCapabilities() 643 & Connection.CAPABILITY_HOLD)) { 644 // This call does not support hold. If it is from a different connection 645 // service, then disconnect it, otherwise allow the connection service to 646 // figure out the right states. 647 if (mForegroundCall.getConnectionService() != call.getConnectionService()) { 648 mForegroundCall.disconnect(); 649 } 650 } else { 651 Call heldCall = getHeldCall(); 652 if (heldCall != null) { 653 Log.v(this, "Disconnecting held call %s before holding active call.", 654 heldCall); 655 heldCall.disconnect(); 656 } 657 658 Log.v(this, "Holding active/dialing call %s before answering incoming call %s.", 659 mForegroundCall, call); 660 mForegroundCall.hold(); 661 } 662 // TODO: Wait until we get confirmation of the active call being 663 // on-hold before answering the new call. 664 // TODO: Import logic from CallManager.acceptCall() 665 } 666 667 for (CallsManagerListener listener : mListeners) { 668 listener.onIncomingCallAnswered(call); 669 } 670 671 // We do not update the UI until we get confirmation of the answer() through 672 // {@link #markCallAsActive}. 673 call.answer(videoState); 674 } 675 } 676 677 /** 678 * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call 679 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 680 * the user opting to reject said call. 681 */ 682 void rejectCall(Call call, boolean rejectWithMessage, String textMessage) { 683 if (!mCalls.contains(call)) { 684 Log.i(this, "Request to reject a non-existent call %s", call); 685 } else { 686 for (CallsManagerListener listener : mListeners) { 687 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage); 688 } 689 call.reject(rejectWithMessage, textMessage); 690 } 691 } 692 693 /** 694 * Instructs Telecom to play the specified DTMF tone within the specified call. 695 * 696 * @param digit The DTMF digit to play. 697 */ 698 void playDtmfTone(Call call, char digit) { 699 if (!mCalls.contains(call)) { 700 Log.i(this, "Request to play DTMF in a non-existent call %s", call); 701 } else { 702 call.playDtmfTone(digit); 703 mDtmfLocalTonePlayer.playTone(call, digit); 704 } 705 } 706 707 /** 708 * Instructs Telecom to stop the currently playing DTMF tone, if any. 709 */ 710 void stopDtmfTone(Call call) { 711 if (!mCalls.contains(call)) { 712 Log.i(this, "Request to stop DTMF in a non-existent call %s", call); 713 } else { 714 call.stopDtmfTone(); 715 mDtmfLocalTonePlayer.stopTone(call); 716 } 717 } 718 719 /** 720 * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any. 721 */ 722 void postDialContinue(Call call, boolean proceed) { 723 if (!mCalls.contains(call)) { 724 Log.i(this, "Request to continue post-dial string in a non-existent call %s", call); 725 } else { 726 call.postDialContinue(proceed); 727 } 728 } 729 730 /** 731 * Instructs Telecom to disconnect the specified call. Intended to be invoked by the 732 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 733 * the user hitting the end-call button. 734 */ 735 void disconnectCall(Call call) { 736 Log.v(this, "disconnectCall %s", call); 737 738 if (!mCalls.contains(call)) { 739 Log.w(this, "Unknown call (%s) asked to disconnect", call); 740 } else { 741 mLocallyDisconnectingCalls.add(call); 742 call.disconnect(); 743 } 744 } 745 746 /** 747 * Instructs Telecom to disconnect all calls. 748 */ 749 void disconnectAllCalls() { 750 Log.v(this, "disconnectAllCalls"); 751 752 for (Call call : mCalls) { 753 disconnectCall(call); 754 } 755 } 756 757 758 /** 759 * Instructs Telecom to put the specified call on hold. Intended to be invoked by the 760 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 761 * the user hitting the hold button during an active call. 762 */ 763 void holdCall(Call call) { 764 if (!mCalls.contains(call)) { 765 Log.w(this, "Unknown call (%s) asked to be put on hold", call); 766 } else { 767 Log.d(this, "Putting call on hold: (%s)", call); 768 call.hold(); 769 } 770 } 771 772 /** 773 * Instructs Telecom to release the specified call from hold. Intended to be invoked by 774 * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered 775 * by the user hitting the hold button during a held call. 776 */ 777 void unholdCall(Call call) { 778 if (!mCalls.contains(call)) { 779 Log.w(this, "Unknown call (%s) asked to be removed from hold", call); 780 } else { 781 Log.d(this, "unholding call: (%s)", call); 782 for (Call c : mCalls) { 783 // Only attempt to hold parent calls and not the individual children. 784 if (c != null && c.isAlive() && c != call && c.getParentCall() == null) { 785 c.hold(); 786 } 787 } 788 call.unhold(); 789 } 790 } 791 792 /** Called by the in-call UI to change the mute state. */ 793 void mute(boolean shouldMute) { 794 mCallAudioManager.mute(shouldMute); 795 } 796 797 /** 798 * Called by the in-call UI to change the audio route, for example to change from earpiece to 799 * speaker phone. 800 */ 801 void setAudioRoute(int route) { 802 mCallAudioManager.setAudioRoute(route); 803 } 804 805 /** Called by the in-call UI to turn the proximity sensor on. */ 806 void turnOnProximitySensor() { 807 mProximitySensorManager.turnOn(); 808 } 809 810 /** 811 * Called by the in-call UI to turn the proximity sensor off. 812 * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise, 813 * the screen will be kept off until the proximity sensor goes negative. 814 */ 815 void turnOffProximitySensor(boolean screenOnImmediately) { 816 mProximitySensorManager.turnOff(screenOnImmediately); 817 } 818 819 void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) { 820 if (!mCalls.contains(call)) { 821 Log.i(this, "Attempted to add account to unknown call %s", call); 822 } else { 823 // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and 824 // the PRE_DIAL_WAIT sequence run in parallel, if the user selects an account before the 825 // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without 826 // respecting a rewritten number or a canceled number. This is unlikely since 827 // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting 828 // a phone account from the in-call UI. 829 call.setTargetPhoneAccount(account); 830 831 // Note: emergency calls never go through account selection dialog so they never 832 // arrive here. 833 if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) { 834 call.startCreateConnection(mPhoneAccountRegistrar); 835 } else { 836 call.disconnect(); 837 } 838 839 if (setDefault) { 840 mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(account); 841 } 842 } 843 } 844 845 /** Called when the audio state changes. */ 846 void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState) { 847 Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState); 848 for (CallsManagerListener listener : mListeners) { 849 listener.onAudioStateChanged(oldAudioState, newAudioState); 850 } 851 } 852 853 void markCallAsRinging(Call call) { 854 setCallState(call, CallState.RINGING); 855 } 856 857 void markCallAsDialing(Call call) { 858 setCallState(call, CallState.DIALING); 859 } 860 861 void markCallAsActive(Call call) { 862 setCallState(call, CallState.ACTIVE); 863 864 if (call.getStartWithSpeakerphoneOn()) { 865 setAudioRoute(AudioState.ROUTE_SPEAKER); 866 } 867 } 868 869 void markCallAsOnHold(Call call) { 870 setCallState(call, CallState.ON_HOLD); 871 } 872 873 /** 874 * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the 875 * last live call, then also disconnect from the in-call controller. 876 * 877 * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}. 878 */ 879 void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) { 880 call.setDisconnectCause(disconnectCause); 881 setCallState(call, CallState.DISCONNECTED); 882 } 883 884 /** 885 * Removes an existing disconnected call, and notifies the in-call app. 886 */ 887 void markCallAsRemoved(Call call) { 888 removeCall(call); 889 if (mLocallyDisconnectingCalls.contains(call)) { 890 mLocallyDisconnectingCalls.remove(call); 891 if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) { 892 mForegroundCall.unhold(); 893 } 894 } 895 } 896 897 /** 898 * Cleans up any calls currently associated with the specified connection service when the 899 * service binder disconnects unexpectedly. 900 * 901 * @param service The connection service that disconnected. 902 */ 903 void handleConnectionServiceDeath(ConnectionServiceWrapper service) { 904 if (service != null) { 905 for (Call call : mCalls) { 906 if (call.getConnectionService() == service) { 907 if (call.getState() != CallState.DISCONNECTED) { 908 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR)); 909 } 910 markCallAsRemoved(call); 911 } 912 } 913 } 914 } 915 916 boolean hasAnyCalls() { 917 return !mCalls.isEmpty(); 918 } 919 920 boolean hasActiveOrHoldingCall() { 921 return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null; 922 } 923 924 boolean hasRingingCall() { 925 return getFirstCallWithState(CallState.RINGING) != null; 926 } 927 928 boolean onMediaButton(int type) { 929 if (hasAnyCalls()) { 930 if (HeadsetMediaButton.SHORT_PRESS == type) { 931 Call ringingCall = getFirstCallWithState(CallState.RINGING); 932 if (ringingCall == null) { 933 mCallAudioManager.toggleMute(); 934 return true; 935 } else { 936 ringingCall.answer(ringingCall.getVideoState()); 937 return true; 938 } 939 } else if (HeadsetMediaButton.LONG_PRESS == type) { 940 Log.d(this, "handleHeadsetHook: longpress -> hangup"); 941 Call callToHangup = getFirstCallWithState( 942 CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD); 943 if (callToHangup != null) { 944 callToHangup.disconnect(); 945 return true; 946 } 947 } 948 } 949 return false; 950 } 951 952 /** 953 * Returns true if telecom supports adding another top-level call. 954 */ 955 boolean canAddCall() { 956 if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) { 957 return false; 958 } 959 960 int count = 0; 961 for (Call call : mCalls) { 962 if (call.isEmergencyCall()) { 963 // We never support add call if one of the calls is an emergency call. 964 return false; 965 } else if (call.getParentCall() == null) { 966 count++; 967 } 968 969 // We do not check states for canAddCall. We treat disconnected calls the same 970 // and wait until they are removed instead. If we didn't count disconnected calls, 971 // we could put InCallServices into a state where they are showing two calls but 972 // also support add-call. Technically it's right, but overall looks better (UI-wise) 973 // and acts better if we wait until the call is removed. 974 if (count >= MAXIMUM_TOP_LEVEL_CALLS) { 975 return false; 976 } 977 } 978 return true; 979 } 980 981 @VisibleForTesting 982 public Call getRingingCall() { 983 return getFirstCallWithState(CallState.RINGING); 984 } 985 986 Call getActiveCall() { 987 return getFirstCallWithState(CallState.ACTIVE); 988 } 989 990 Call getDialingCall() { 991 return getFirstCallWithState(CallState.DIALING); 992 } 993 994 Call getHeldCall() { 995 return getFirstCallWithState(CallState.ON_HOLD); 996 } 997 998 int getNumHeldCalls() { 999 int count = 0; 1000 for (Call call : mCalls) { 1001 if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) { 1002 count++; 1003 } 1004 } 1005 return count; 1006 } 1007 1008 Call getFirstCallWithState(int... states) { 1009 return getFirstCallWithState(null, states); 1010 } 1011 1012 /** 1013 * Returns the first call that it finds with the given states. The states are treated as having 1014 * priority order so that any call with the first state will be returned before any call with 1015 * states listed later in the parameter list. 1016 * 1017 * @param callToSkip Call that this method should skip while searching 1018 */ 1019 Call getFirstCallWithState(Call callToSkip, int... states) { 1020 for (int currentState : states) { 1021 // check the foreground first 1022 if (mForegroundCall != null && mForegroundCall.getState() == currentState) { 1023 return mForegroundCall; 1024 } 1025 1026 for (Call call : mCalls) { 1027 if (Objects.equals(callToSkip, call)) { 1028 continue; 1029 } 1030 1031 // Only operate on top-level calls 1032 if (call.getParentCall() != null) { 1033 continue; 1034 } 1035 1036 if (currentState == call.getState()) { 1037 return call; 1038 } 1039 } 1040 } 1041 return null; 1042 } 1043 1044 Call createConferenceCall( 1045 PhoneAccountHandle phoneAccount, 1046 ParcelableConference parcelableConference) { 1047 1048 // If the parceled conference specifies a connect time, use it; otherwise default to 0, 1049 // which is the default value for new Calls. 1050 long connectTime = 1051 parcelableConference.getConnectTimeMillis() == 1052 Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 : 1053 parcelableConference.getConnectTimeMillis(); 1054 1055 Call call = new Call( 1056 mContext, 1057 this, 1058 mConnectionServiceRepository, 1059 null /* handle */, 1060 null /* gatewayInfo */, 1061 null /* connectionManagerPhoneAccount */, 1062 phoneAccount, 1063 false /* isIncoming */, 1064 true /* isConference */, 1065 connectTime); 1066 1067 setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState())); 1068 call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities()); 1069 1070 // TODO: Move this to be a part of addCall() 1071 call.addListener(this); 1072 addCall(call); 1073 return call; 1074 } 1075 1076 /** 1077 * @return the call state currently tracked by {@link PhoneStateBroadcaster} 1078 */ 1079 int getCallState() { 1080 return mPhoneStateBroadcaster.getCallState(); 1081 } 1082 1083 /** 1084 * Retrieves the {@link PhoneAccountRegistrar}. 1085 * 1086 * @return The {@link PhoneAccountRegistrar}. 1087 */ 1088 PhoneAccountRegistrar getPhoneAccountRegistrar() { 1089 return mPhoneAccountRegistrar; 1090 } 1091 1092 /** 1093 * Retrieves the {@link MissedCallNotifier} 1094 * @return The {@link MissedCallNotifier}. 1095 */ 1096 MissedCallNotifier getMissedCallNotifier() { 1097 return mMissedCallNotifier; 1098 } 1099 1100 /** 1101 * Adds the specified call to the main list of live calls. 1102 * 1103 * @param call The call to add. 1104 */ 1105 private void addCall(Call call) { 1106 Trace.beginSection("addCall"); 1107 Log.v(this, "addCall(%s)", call); 1108 call.addListener(this); 1109 mCalls.add(call); 1110 1111 // TODO: Update mForegroundCall prior to invoking 1112 // onCallAdded for calls which immediately take the foreground (like the first call). 1113 for (CallsManagerListener listener : mListeners) { 1114 if (Log.SYSTRACE_DEBUG) { 1115 Trace.beginSection(listener.getClass().toString() + " addCall"); 1116 } 1117 listener.onCallAdded(call); 1118 if (Log.SYSTRACE_DEBUG) { 1119 Trace.endSection(); 1120 } 1121 } 1122 updateCallsManagerState(); 1123 Trace.endSection(); 1124 } 1125 1126 private void removeCall(Call call) { 1127 Trace.beginSection("removeCall"); 1128 Log.v(this, "removeCall(%s)", call); 1129 1130 call.setParentCall(null); // need to clean up parent relationship before destroying. 1131 call.removeListener(this); 1132 call.clearConnectionService(); 1133 1134 boolean shouldNotify = false; 1135 if (mCalls.contains(call)) { 1136 mCalls.remove(call); 1137 shouldNotify = true; 1138 } 1139 1140 // Only broadcast changes for calls that are being tracked. 1141 if (shouldNotify) { 1142 for (CallsManagerListener listener : mListeners) { 1143 if (Log.SYSTRACE_DEBUG) { 1144 Trace.beginSection(listener.getClass().toString() + " onCallRemoved"); 1145 } 1146 listener.onCallRemoved(call); 1147 if (Log.SYSTRACE_DEBUG) { 1148 Trace.endSection(); 1149 } 1150 } 1151 updateCallsManagerState(); 1152 } 1153 Trace.endSection(); 1154 } 1155 1156 /** 1157 * Sets the specified state on the specified call. 1158 * 1159 * @param call The call. 1160 * @param newState The new state of the call. 1161 */ 1162 private void setCallState(Call call, int newState) { 1163 if (call == null) { 1164 return; 1165 } 1166 int oldState = call.getState(); 1167 Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState), 1168 CallState.toString(newState), call); 1169 if (newState != oldState) { 1170 // Unfortunately, in the telephony world the radio is king. So if the call notifies 1171 // us that the call is in a particular state, we allow it even if it doesn't make 1172 // sense (e.g., STATE_ACTIVE -> STATE_RINGING). 1173 // TODO: Consider putting a stop to the above and turning CallState 1174 // into a well-defined state machine. 1175 // TODO: Define expected state transitions here, and log when an 1176 // unexpected transition occurs. 1177 call.setState(newState); 1178 1179 Trace.beginSection("onCallStateChanged"); 1180 // Only broadcast state change for calls that are being tracked. 1181 if (mCalls.contains(call)) { 1182 for (CallsManagerListener listener : mListeners) { 1183 if (Log.SYSTRACE_DEBUG) { 1184 Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); 1185 } 1186 listener.onCallStateChanged(call, oldState, newState); 1187 if (Log.SYSTRACE_DEBUG) { 1188 Trace.endSection(); 1189 } 1190 } 1191 updateCallsManagerState(); 1192 } 1193 Trace.endSection(); 1194 } 1195 } 1196 1197 /** 1198 * Checks which call should be visible to the user and have audio focus. 1199 */ 1200 private void updateForegroundCall() { 1201 Trace.beginSection("updateForegroundCall"); 1202 Call newForegroundCall = null; 1203 for (Call call : mCalls) { 1204 // TODO: Foreground-ness needs to be explicitly set. No call, regardless 1205 // of its state will be foreground by default and instead the connection service should 1206 // be notified when its calls enter and exit foreground state. Foreground will mean that 1207 // the call should play audio and listen to microphone if it wants. 1208 1209 // Only top-level calls can be in foreground 1210 if (call.getParentCall() != null) { 1211 continue; 1212 } 1213 1214 // Active calls have priority. 1215 if (call.isActive()) { 1216 newForegroundCall = call; 1217 break; 1218 } 1219 1220 if (call.isAlive() || call.getState() == CallState.RINGING) { 1221 newForegroundCall = call; 1222 // Don't break in case there's an active call that has priority. 1223 } 1224 } 1225 1226 if (newForegroundCall != mForegroundCall) { 1227 Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall); 1228 Call oldForegroundCall = mForegroundCall; 1229 mForegroundCall = newForegroundCall; 1230 1231 for (CallsManagerListener listener : mListeners) { 1232 if (Log.SYSTRACE_DEBUG) { 1233 Trace.beginSection(listener.getClass().toString() + " updateForegroundCall"); 1234 } 1235 listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall); 1236 if (Log.SYSTRACE_DEBUG) { 1237 Trace.endSection(); 1238 } 1239 } 1240 } 1241 Trace.endSection(); 1242 } 1243 1244 private void updateCanAddCall() { 1245 boolean newCanAddCall = canAddCall(); 1246 if (newCanAddCall != mCanAddCall) { 1247 mCanAddCall = newCanAddCall; 1248 for (CallsManagerListener listener : mListeners) { 1249 if (Log.SYSTRACE_DEBUG) { 1250 Trace.beginSection(listener.getClass().toString() + " updateCanAddCall"); 1251 } 1252 listener.onCanAddCallChanged(mCanAddCall); 1253 if (Log.SYSTRACE_DEBUG) { 1254 Trace.endSection(); 1255 } 1256 } 1257 } 1258 } 1259 1260 private void updateCallsManagerState() { 1261 updateForegroundCall(); 1262 updateCanAddCall(); 1263 } 1264 1265 private boolean isPotentialMMICode(Uri handle) { 1266 return (handle != null && handle.getSchemeSpecificPart() != null 1267 && handle.getSchemeSpecificPart().contains("#")); 1268 } 1269 1270 /** 1271 * Determines if a dialed number is potentially an In-Call MMI code. In-Call MMI codes are 1272 * MMI codes which can be dialed when one or more calls are in progress. 1273 * <P> 1274 * Checks for numbers formatted similar to the MMI codes defined in: 1275 * {@link com.android.internal.telephony.gsm.GSMPhone#handleInCallMmiCommands(String)} 1276 * and 1277 * {@link com.android.internal.telephony.imsphone.ImsPhone#handleInCallMmiCommands(String)} 1278 * 1279 * @param handle The URI to call. 1280 * @return {@code True} if the URI represents a number which could be an in-call MMI code. 1281 */ 1282 private boolean isPotentialInCallMMICode(Uri handle) { 1283 if (handle != null && handle.getSchemeSpecificPart() != null && 1284 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) { 1285 1286 String dialedNumber = handle.getSchemeSpecificPart(); 1287 return (dialedNumber.equals("0") || 1288 (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) || 1289 (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) || 1290 dialedNumber.equals("3") || 1291 dialedNumber.equals("4") || 1292 dialedNumber.equals("5")); 1293 } 1294 return false; 1295 } 1296 1297 private int getNumCallsWithState(int... states) { 1298 int count = 0; 1299 for (int state : states) { 1300 for (Call call : mCalls) { 1301 if (call.getParentCall() == null && call.getState() == state) { 1302 count++; 1303 } 1304 } 1305 } 1306 return count; 1307 } 1308 1309 private boolean hasMaximumLiveCalls() { 1310 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES); 1311 } 1312 1313 private boolean hasMaximumHoldingCalls() { 1314 return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD); 1315 } 1316 1317 private boolean hasMaximumRingingCalls() { 1318 return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING); 1319 } 1320 1321 private boolean hasMaximumOutgoingCalls() { 1322 return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES); 1323 } 1324 1325 private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) { 1326 if (hasMaximumLiveCalls()) { 1327 // NOTE: If the amount of live calls changes beyond 1, this logic will probably 1328 // have to change. 1329 Call liveCall = getFirstCallWithState(call, LIVE_CALL_STATES); 1330 Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " + 1331 liveCall); 1332 1333 if (call == liveCall) { 1334 // If the call is already the foreground call, then we are golden. 1335 // This can happen after the user selects an account in the PRE_DIAL_WAIT 1336 // state since the call was already populated into the list. 1337 return true; 1338 } 1339 1340 if (hasMaximumOutgoingCalls()) { 1341 // Disconnect the current outgoing call if it's not an emergency call. If the user 1342 // tries to make two outgoing calls to different emergency call numbers, we will try 1343 // to connect the first outgoing call. 1344 if (isEmergency) { 1345 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES); 1346 if (!outgoingCall.isEmergencyCall()) { 1347 outgoingCall.disconnect(); 1348 return true; 1349 } 1350 } 1351 return false; 1352 } 1353 1354 if (hasMaximumHoldingCalls()) { 1355 // There is no more room for any more calls, unless it's an emergency. 1356 if (isEmergency) { 1357 // Kill the current active call, this is easier then trying to disconnect a 1358 // holding call and hold an active call. 1359 liveCall.disconnect(); 1360 return true; 1361 } 1362 return false; // No more room! 1363 } 1364 1365 // We have room for at least one more holding call at this point. 1366 1367 // First thing, if we are trying to make a call with the same phone account as the live 1368 // call, then allow it so that the connection service can make its own decision about 1369 // how to handle the new call relative to the current one. 1370 if (Objects.equals(liveCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { 1371 return true; 1372 } else if (call.getTargetPhoneAccount() == null) { 1373 // Without a phone account, we can't say reliably that the call will fail. 1374 // If the user chooses the same phone account as the live call, then it's 1375 // still possible that the call can be made (like with CDMA calls not supporting 1376 // hold but they still support adding a call by going immediately into conference 1377 // mode). Return true here and we'll run this code again after user chooses an 1378 // account. 1379 return true; 1380 } 1381 1382 // Try to hold the live call before attempting the new outgoing call. 1383 if (liveCall.can(Connection.CAPABILITY_HOLD)) { 1384 liveCall.hold(); 1385 return true; 1386 } 1387 1388 // The live call cannot be held so we're out of luck here. There's no room. 1389 return false; 1390 } 1391 return true; 1392 } 1393 1394 /** 1395 * Creates a new call for an existing connection. 1396 * 1397 * @param callId The id of the new call. 1398 * @param connection The connection information. 1399 * @return The new call. 1400 */ 1401 Call createCallForExistingConnection(String callId, ParcelableConnection connection) { 1402 Call call = new Call( 1403 mContext, 1404 this, 1405 mConnectionServiceRepository, 1406 connection.getHandle() /* handle */, 1407 null /* gatewayInfo */, 1408 null /* connectionManagerPhoneAccount */, 1409 connection.getPhoneAccount(), /* targetPhoneAccountHandle */ 1410 false /* isIncoming */, 1411 false /* isConference */); 1412 1413 setCallState(call, Call.getStateFromConnectionState(connection.getState())); 1414 call.setConnectionCapabilities(connection.getConnectionCapabilities()); 1415 call.setCallerDisplayName(connection.getCallerDisplayName(), 1416 connection.getCallerDisplayNamePresentation()); 1417 1418 call.addListener(this); 1419 addCall(call); 1420 1421 return call; 1422 } 1423 1424 /** 1425 * Dumps the state of the {@link CallsManager}. 1426 * 1427 * @param pw The {@code IndentingPrintWriter} to write the state to. 1428 */ 1429 public void dump(IndentingPrintWriter pw) { 1430 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 1431 if (mCalls != null) { 1432 pw.println("mCalls: "); 1433 pw.increaseIndent(); 1434 for (Call call : mCalls) { 1435 pw.println(call); 1436 } 1437 pw.decreaseIndent(); 1438 } 1439 pw.println("mForegroundCall: " + (mForegroundCall == null ? "none" : mForegroundCall)); 1440 1441 if (mCallAudioManager != null) { 1442 pw.println("mCallAudioManager:"); 1443 pw.increaseIndent(); 1444 mCallAudioManager.dump(pw); 1445 pw.decreaseIndent(); 1446 } 1447 1448 if (mTtyManager != null) { 1449 pw.println("mTtyManager:"); 1450 pw.increaseIndent(); 1451 mTtyManager.dump(pw); 1452 pw.decreaseIndent(); 1453 } 1454 1455 if (mInCallController != null) { 1456 pw.println("mInCallController:"); 1457 pw.increaseIndent(); 1458 mInCallController.dump(pw); 1459 pw.decreaseIndent(); 1460 } 1461 1462 if (mConnectionServiceRepository != null) { 1463 pw.println("mConnectionServiceRepository:"); 1464 pw.increaseIndent(); 1465 mConnectionServiceRepository.dump(pw); 1466 pw.decreaseIndent(); 1467 } 1468 } 1469} 1470