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