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