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