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