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