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