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