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