CallsManager.java revision b8ce2aa67dd33416bbea42a848e9d4a8e79b5de2
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.bluetooth.BluetoothRouteManager; 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; 68import com.android.server.telecom.ui.ConfirmCallDialogActivity; 69import com.android.server.telecom.ui.IncomingCallNotifier; 70 71import java.util.ArrayList; 72import java.util.Arrays; 73import java.util.Collection; 74import java.util.Collections; 75import java.util.HashMap; 76import java.util.HashSet; 77import java.util.Iterator; 78import java.util.List; 79import java.util.Map; 80import java.util.Objects; 81import java.util.Optional; 82import java.util.Set; 83import java.util.concurrent.ConcurrentHashMap; 84import java.util.concurrent.CountDownLatch; 85import java.util.concurrent.TimeUnit; 86import java.util.stream.Collectors; 87import java.util.stream.IntStream; 88import java.util.stream.Stream; 89 90/** 91 * Singleton. 92 * 93 * NOTE: by design most APIs are package private, use the relevant adapter/s to allow 94 * access from other packages specifically refraining from passing the CallsManager instance 95 * beyond the com.android.server.telecom package boundary. 96 */ 97@VisibleForTesting 98public class CallsManager extends Call.ListenerBase 99 implements VideoProviderProxy.Listener, CallFilterResultCallback, CurrentUserProxy { 100 101 // TODO: Consider renaming this CallsManagerPlugin. 102 @VisibleForTesting 103 public interface CallsManagerListener { 104 void onCallAdded(Call call); 105 void onCallRemoved(Call call); 106 void onCallStateChanged(Call call, int oldState, int newState); 107 void onConnectionServiceChanged( 108 Call call, 109 ConnectionServiceWrapper oldService, 110 ConnectionServiceWrapper newService); 111 void onIncomingCallAnswered(Call call); 112 void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage); 113 void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState); 114 void onRingbackRequested(Call call, boolean ringback); 115 void onIsConferencedChanged(Call call); 116 void onIsVoipAudioModeChanged(Call call); 117 void onVideoStateChanged(Call call, int previousVideoState, int newVideoState); 118 void onCanAddCallChanged(boolean canAddCall); 119 void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile); 120 void onHoldToneRequested(Call call); 121 void onExternalCallChanged(Call call, boolean isExternalCall); 122 } 123 124 private static final String TAG = "CallsManager"; 125 126 /** 127 * Call filter specifier used with 128 * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate only 129 * self-managed calls should be included. 130 */ 131 private static final int CALL_FILTER_SELF_MANAGED = 1; 132 133 /** 134 * Call filter specifier used with 135 * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate only 136 * managed calls should be included. 137 */ 138 private static final int CALL_FILTER_MANAGED = 2; 139 140 /** 141 * Call filter specifier used with 142 * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate both managed 143 * and self-managed calls should be included. 144 */ 145 private static final int CALL_FILTER_ALL = 3; 146 147 private static final String PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION = 148 "android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION"; 149 150 private static final int HANDLER_WAIT_TIMEOUT = 10000; 151 private static final int MAXIMUM_LIVE_CALLS = 1; 152 private static final int MAXIMUM_HOLD_CALLS = 1; 153 private static final int MAXIMUM_RINGING_CALLS = 1; 154 private static final int MAXIMUM_DIALING_CALLS = 1; 155 private static final int MAXIMUM_OUTGOING_CALLS = 1; 156 private static final int MAXIMUM_TOP_LEVEL_CALLS = 2; 157 private static final int MAXIMUM_SELF_MANAGED_CALLS = 10; 158 159 private static final int[] OUTGOING_CALL_STATES = 160 {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, 161 CallState.PULLING}; 162 163 /** 164 * These states are used by {@link #makeRoomForOutgoingCall(Call, boolean)} to determine which 165 * call should be ended first to make room for a new outgoing call. 166 */ 167 private static final int[] LIVE_CALL_STATES = 168 {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, 169 CallState.PULLING, CallState.ACTIVE}; 170 171 /** 172 * These states determine which calls will cause {@link TelecomManager#isInCall()} or 173 * {@link TelecomManager#isInManagedCall()} to return true. 174 * 175 * See also {@link PhoneStateBroadcaster}, which considers a similar set of states as being 176 * off-hook. 177 */ 178 public static final int[] ONGOING_CALL_STATES = 179 {CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, CallState.PULLING, CallState.ACTIVE, 180 CallState.ON_HOLD, CallState.RINGING}; 181 182 private static final int[] ANY_CALL_STATE = 183 {CallState.NEW, CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, 184 CallState.RINGING, CallState.ACTIVE, CallState.ON_HOLD, CallState.DISCONNECTED, 185 CallState.ABORTED, CallState.DISCONNECTING, CallState.PULLING}; 186 187 public static final String TELECOM_CALL_ID_PREFIX = "TC@"; 188 189 // Maps call technologies in PhoneConstants to those in Analytics. 190 private static final Map<Integer, Integer> sAnalyticsTechnologyMap; 191 static { 192 sAnalyticsTechnologyMap = new HashMap<>(5); 193 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE); 194 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_GSM, Analytics.GSM_PHONE); 195 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_IMS, Analytics.IMS_PHONE); 196 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_SIP, Analytics.SIP_PHONE); 197 sAnalyticsTechnologyMap.put(PhoneConstants.PHONE_TYPE_THIRD_PARTY, 198 Analytics.THIRD_PARTY_PHONE); 199 } 200 201 /** 202 * The main call repository. Keeps an instance of all live calls. New incoming and outgoing 203 * calls are added to the map and removed when the calls move to the disconnected state. 204 * 205 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is 206 * load factor before resizing, 1 means we only expect a single thread to 207 * access the map so make only a single shard 208 */ 209 private final Set<Call> mCalls = Collections.newSetFromMap( 210 new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1)); 211 212 /** 213 * A pending call is one which requires user-intervention in order to be placed. 214 * Used by {@link #startCallConfirmation(Call)}. 215 */ 216 private Call mPendingCall; 217 218 /** 219 * The current telecom call ID. Used when creating new instances of {@link Call}. Should 220 * only be accessed using the {@link #getNextCallId()} method which synchronizes on the 221 * {@link #mLock} sync root. 222 */ 223 private int mCallId = 0; 224 225 private int mRttRequestId = 0; 226 /** 227 * Stores the current foreground user. 228 */ 229 private UserHandle mCurrentUserHandle = UserHandle.of(ActivityManager.getCurrentUser()); 230 231 private final ConnectionServiceRepository mConnectionServiceRepository; 232 private final DtmfLocalTonePlayer mDtmfLocalTonePlayer; 233 private final InCallController mInCallController; 234 private final CallAudioManager mCallAudioManager; 235 private RespondViaSmsManager mRespondViaSmsManager; 236 private final Ringer mRinger; 237 private final InCallWakeLockController mInCallWakeLockController; 238 // For this set initial table size to 16 because we add 13 listeners in 239 // the CallsManager constructor. 240 private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap( 241 new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1)); 242 private final HeadsetMediaButton mHeadsetMediaButton; 243 private final WiredHeadsetManager mWiredHeadsetManager; 244 private final BluetoothRouteManager mBluetoothRouteManager; 245 private final DockManager mDockManager; 246 private final TtyManager mTtyManager; 247 private final ProximitySensorManager mProximitySensorManager; 248 private final PhoneStateBroadcaster mPhoneStateBroadcaster; 249 private final CallLogManager mCallLogManager; 250 private final Context mContext; 251 private final TelecomSystem.SyncRoot mLock; 252 private final ContactsAsyncHelper mContactsAsyncHelper; 253 private final CallerInfoAsyncQueryFactory mCallerInfoAsyncQueryFactory; 254 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 255 private final MissedCallNotifier mMissedCallNotifier; 256 private IncomingCallNotifier mIncomingCallNotifier; 257 private final CallerInfoLookupHelper mCallerInfoLookupHelper; 258 private final DefaultDialerCache mDefaultDialerCache; 259 private final Timeouts.Adapter mTimeoutsAdapter; 260 private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter; 261 private final ClockProxy mClockProxy; 262 private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>(); 263 private final Set<Call> mPendingCallsToDisconnect = new HashSet<>(); 264 /* Handler tied to thread in which CallManager was initialized. */ 265 private final Handler mHandler = new Handler(Looper.getMainLooper()); 266 private final EmergencyCallHelper mEmergencyCallHelper; 267 268 private boolean mCanAddCall = true; 269 270 private TelephonyManager.MultiSimVariants mRadioSimVariants = null; 271 272 private Runnable mStopTone; 273 274 /** 275 * Listener to PhoneAccountRegistrar events. 276 */ 277 private PhoneAccountRegistrar.Listener mPhoneAccountListener = 278 new PhoneAccountRegistrar.Listener() { 279 public void onPhoneAccountRegistered(PhoneAccountRegistrar registrar, 280 PhoneAccountHandle handle) { 281 broadcastRegisterIntent(handle); 282 } 283 public void onPhoneAccountUnRegistered(PhoneAccountRegistrar registrar, 284 PhoneAccountHandle handle) { 285 broadcastUnregisterIntent(handle); 286 } 287 }; 288 289 /** 290 * Initializes the required Telecom components. 291 */ 292 @VisibleForTesting 293 public CallsManager( 294 Context context, 295 TelecomSystem.SyncRoot lock, 296 ContactsAsyncHelper contactsAsyncHelper, 297 CallerInfoAsyncQueryFactory callerInfoAsyncQueryFactory, 298 MissedCallNotifier missedCallNotifier, 299 PhoneAccountRegistrar phoneAccountRegistrar, 300 HeadsetMediaButtonFactory headsetMediaButtonFactory, 301 ProximitySensorManagerFactory proximitySensorManagerFactory, 302 InCallWakeLockControllerFactory inCallWakeLockControllerFactory, 303 CallAudioManager.AudioServiceFactory audioServiceFactory, 304 BluetoothRouteManager bluetoothManager, 305 WiredHeadsetManager wiredHeadsetManager, 306 SystemStateProvider systemStateProvider, 307 DefaultDialerCache defaultDialerCache, 308 Timeouts.Adapter timeoutsAdapter, 309 AsyncRingtonePlayer asyncRingtonePlayer, 310 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, 311 EmergencyCallHelper emergencyCallHelper, 312 InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory, 313 ClockProxy clockProxy, 314 InCallControllerFactory inCallControllerFactory) { 315 mContext = context; 316 mLock = lock; 317 mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter; 318 mContactsAsyncHelper = contactsAsyncHelper; 319 mCallerInfoAsyncQueryFactory = callerInfoAsyncQueryFactory; 320 mPhoneAccountRegistrar = phoneAccountRegistrar; 321 mPhoneAccountRegistrar.addListener(mPhoneAccountListener); 322 mMissedCallNotifier = missedCallNotifier; 323 StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this); 324 mWiredHeadsetManager = wiredHeadsetManager; 325 mDefaultDialerCache = defaultDialerCache; 326 mBluetoothRouteManager = bluetoothManager; 327 mDockManager = new DockManager(context); 328 mTimeoutsAdapter = timeoutsAdapter; 329 mEmergencyCallHelper = emergencyCallHelper; 330 mCallerInfoLookupHelper = new CallerInfoLookupHelper(context, mCallerInfoAsyncQueryFactory, 331 mContactsAsyncHelper, mLock); 332 333 mDtmfLocalTonePlayer = 334 new DtmfLocalTonePlayer(new DtmfLocalTonePlayer.ToneGeneratorProxy()); 335 CallAudioRouteStateMachine callAudioRouteStateMachine = new CallAudioRouteStateMachine( 336 context, 337 this, 338 bluetoothManager, 339 wiredHeadsetManager, 340 statusBarNotifier, 341 audioServiceFactory, 342 CallAudioRouteStateMachine.doesDeviceSupportEarpieceRoute() 343 ); 344 callAudioRouteStateMachine.initialize(); 345 346 CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter = 347 new CallAudioRoutePeripheralAdapter( 348 callAudioRouteStateMachine, 349 bluetoothManager, 350 wiredHeadsetManager, 351 mDockManager); 352 353 InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory( 354 callAudioRoutePeripheralAdapter, lock, toneGeneratorFactory); 355 356 SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil(); 357 RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context); 358 SystemVibrator systemVibrator = new SystemVibrator(context); 359 mInCallController = inCallControllerFactory.create(context, mLock, this, 360 systemStateProvider, defaultDialerCache, mTimeoutsAdapter, 361 emergencyCallHelper); 362 mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer, 363 ringtoneFactory, systemVibrator, mInCallController); 364 365 mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine, 366 this,new CallAudioModeStateMachine((AudioManager) 367 mContext.getSystemService(Context.AUDIO_SERVICE)), 368 playerFactory, mRinger, new RingbackPlayer(playerFactory), mDtmfLocalTonePlayer); 369 370 mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock); 371 mTtyManager = new TtyManager(context, mWiredHeadsetManager); 372 mProximitySensorManager = proximitySensorManagerFactory.create(context, this); 373 mPhoneStateBroadcaster = new PhoneStateBroadcaster(this); 374 mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier); 375 mConnectionServiceRepository = 376 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this); 377 mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this); 378 mClockProxy = clockProxy; 379 380 mListeners.add(mInCallWakeLockController); 381 mListeners.add(statusBarNotifier); 382 mListeners.add(mCallLogManager); 383 mListeners.add(mPhoneStateBroadcaster); 384 mListeners.add(mInCallController); 385 mListeners.add(mCallAudioManager); 386 mListeners.add(missedCallNotifier); 387 mListeners.add(mHeadsetMediaButton); 388 mListeners.add(mProximitySensorManager); 389 390 // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly. 391 final UserManager userManager = UserManager.get(mContext); 392 // Don't load missed call if it is run in split user model. 393 if (userManager.isPrimaryUser()) { 394 onUserSwitch(Process.myUserHandle()); 395 } 396 } 397 398 public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) { 399 if (mIncomingCallNotifier != null) { 400 mListeners.remove(mIncomingCallNotifier); 401 } 402 mIncomingCallNotifier = incomingCallNotifier; 403 mListeners.add(mIncomingCallNotifier); 404 } 405 406 public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) { 407 if (mRespondViaSmsManager != null) { 408 mListeners.remove(mRespondViaSmsManager); 409 } 410 mRespondViaSmsManager = respondViaSmsManager; 411 mListeners.add(respondViaSmsManager); 412 } 413 414 public RespondViaSmsManager getRespondViaSmsManager() { 415 return mRespondViaSmsManager; 416 } 417 418 public CallerInfoLookupHelper getCallerInfoLookupHelper() { 419 return mCallerInfoLookupHelper; 420 } 421 422 @Override 423 public void onSuccessfulOutgoingCall(Call call, int callState) { 424 Log.v(this, "onSuccessfulOutgoingCall, %s", call); 425 426 setCallState(call, callState, "successful outgoing call"); 427 if (!mCalls.contains(call)) { 428 // Call was not added previously in startOutgoingCall due to it being a potential MMI 429 // code, so add it now. 430 addCall(call); 431 } 432 433 // The call's ConnectionService has been updated. 434 for (CallsManagerListener listener : mListeners) { 435 listener.onConnectionServiceChanged(call, null, call.getConnectionService()); 436 } 437 438 markCallAsDialing(call); 439 } 440 441 @Override 442 public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) { 443 Log.v(this, "onFailedOutgoingCall, call: %s", call); 444 445 markCallAsRemoved(call); 446 } 447 448 @Override 449 public void onSuccessfulIncomingCall(Call incomingCall) { 450 Log.d(this, "onSuccessfulIncomingCall"); 451 if (incomingCall.hasProperty(Connection.PROPERTY_EMERGENCY_CALLBACK_MODE)) { 452 Log.i(this, "Skipping call filtering due to ECBM"); 453 onCallFilteringComplete(incomingCall, new CallFilteringResult(true, false, true, true)); 454 return; 455 } 456 457 List<IncomingCallFilter.CallFilter> filters = new ArrayList<>(); 458 filters.add(new DirectToVoicemailCallFilter(mCallerInfoLookupHelper)); 459 filters.add(new AsyncBlockCheckFilter(mContext, new BlockCheckerAdapter())); 460 filters.add(new CallScreeningServiceFilter(mContext, this, mPhoneAccountRegistrar, 461 mDefaultDialerCache, new ParcelableCallUtils.Converter(), mLock)); 462 new IncomingCallFilter(mContext, this, incomingCall, mLock, 463 mTimeoutsAdapter, filters).performFiltering(); 464 } 465 466 @Override 467 public void onCallFilteringComplete(Call incomingCall, CallFilteringResult result) { 468 // Only set the incoming call as ringing if it isn't already disconnected. It is possible 469 // that the connection service disconnected the call before it was even added to Telecom, in 470 // which case it makes no sense to set it back to a ringing state. 471 if (incomingCall.getState() != CallState.DISCONNECTED && 472 incomingCall.getState() != CallState.DISCONNECTING) { 473 setCallState(incomingCall, CallState.RINGING, 474 result.shouldAllowCall ? "successful incoming call" : "blocking call"); 475 } else { 476 Log.i(this, "onCallFilteringCompleted: call already disconnected."); 477 return; 478 } 479 480 if (result.shouldAllowCall) { 481 if (hasMaximumManagedRingingCalls(incomingCall)) { 482 if (shouldSilenceInsteadOfReject(incomingCall)) { 483 incomingCall.silence(); 484 } else { 485 Log.i(this, "onCallFilteringCompleted: Call rejected! " + 486 "Exceeds maximum number of ringing calls."); 487 rejectCallAndLog(incomingCall); 488 } 489 } else if (hasMaximumManagedDialingCalls(incomingCall)) { 490 Log.i(this, "onCallFilteringCompleted: Call rejected! Exceeds maximum number of " + 491 "dialing calls."); 492 rejectCallAndLog(incomingCall); 493 } else { 494 addCall(incomingCall); 495 } 496 } else { 497 if (result.shouldReject) { 498 Log.i(this, "onCallFilteringCompleted: blocked call, rejecting."); 499 incomingCall.reject(false, null); 500 } 501 if (result.shouldAddToCallLog) { 502 Log.i(this, "onCallScreeningCompleted: blocked call, adding to call log."); 503 if (result.shouldShowNotification) { 504 Log.w(this, "onCallScreeningCompleted: blocked call, showing notification."); 505 } 506 mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE, 507 result.shouldShowNotification); 508 } else if (result.shouldShowNotification) { 509 Log.i(this, "onCallScreeningCompleted: blocked call, showing notification."); 510 mMissedCallNotifier.showMissedCallNotification( 511 new MissedCallNotifier.CallInfo(incomingCall)); 512 } 513 } 514 } 515 516 /** 517 * Whether allow (silence rather than reject) the incoming call if it has a different source 518 * (connection service) from the existing ringing call when reaching maximum ringing calls. 519 */ 520 private boolean shouldSilenceInsteadOfReject(Call incomingCall) { 521 if (!mContext.getResources().getBoolean( 522 R.bool.silence_incoming_when_different_service_and_maximum_ringing)) { 523 return false; 524 } 525 526 Call ringingCall = null; 527 528 for (Call call : mCalls) { 529 // Only operate on top-level calls 530 if (call.getParentCall() != null) { 531 continue; 532 } 533 534 if (call.isExternalCall()) { 535 continue; 536 } 537 538 if (CallState.RINGING == call.getState() && 539 call.getConnectionService() == incomingCall.getConnectionService()) { 540 return false; 541 } 542 } 543 544 return true; 545 } 546 547 @Override 548 public void onFailedIncomingCall(Call call) { 549 setCallState(call, CallState.DISCONNECTED, "failed incoming call"); 550 call.removeListener(this); 551 } 552 553 @Override 554 public void onSuccessfulUnknownCall(Call call, int callState) { 555 setCallState(call, callState, "successful unknown call"); 556 Log.i(this, "onSuccessfulUnknownCall for call %s", call); 557 addCall(call); 558 } 559 560 @Override 561 public void onFailedUnknownCall(Call call) { 562 Log.i(this, "onFailedUnknownCall for call %s", call); 563 setCallState(call, CallState.DISCONNECTED, "failed unknown call"); 564 call.removeListener(this); 565 } 566 567 @Override 568 public void onRingbackRequested(Call call, boolean ringback) { 569 for (CallsManagerListener listener : mListeners) { 570 listener.onRingbackRequested(call, ringback); 571 } 572 } 573 574 @Override 575 public void onPostDialWait(Call call, String remaining) { 576 mInCallController.onPostDialWait(call, remaining); 577 } 578 579 @Override 580 public void onPostDialChar(final Call call, char nextChar) { 581 if (PhoneNumberUtils.is12Key(nextChar)) { 582 // Play tone if it is one of the dialpad digits, canceling out the previously queued 583 // up stopTone runnable since playing a new tone automatically stops the previous tone. 584 if (mStopTone != null) { 585 mHandler.removeCallbacks(mStopTone.getRunnableToCancel()); 586 mStopTone.cancel(); 587 } 588 589 mDtmfLocalTonePlayer.playTone(call, nextChar); 590 591 mStopTone = new Runnable("CM.oPDC", mLock) { 592 @Override 593 public void loggedRun() { 594 // Set a timeout to stop the tone in case there isn't another tone to 595 // follow. 596 mDtmfLocalTonePlayer.stopTone(call); 597 } 598 }; 599 mHandler.postDelayed(mStopTone.prepare(), 600 Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver())); 601 } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT || 602 nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) { 603 // Stop the tone if a tone is playing, removing any other stopTone callbacks since 604 // the previous tone is being stopped anyway. 605 if (mStopTone != null) { 606 mHandler.removeCallbacks(mStopTone.getRunnableToCancel()); 607 mStopTone.cancel(); 608 } 609 mDtmfLocalTonePlayer.stopTone(call); 610 } else { 611 Log.w(this, "onPostDialChar: invalid value %d", nextChar); 612 } 613 } 614 615 @Override 616 public void onParentChanged(Call call) { 617 // parent-child relationship affects which call should be foreground, so do an update. 618 updateCanAddCall(); 619 for (CallsManagerListener listener : mListeners) { 620 listener.onIsConferencedChanged(call); 621 } 622 } 623 624 @Override 625 public void onChildrenChanged(Call call) { 626 // parent-child relationship affects which call should be foreground, so do an update. 627 updateCanAddCall(); 628 for (CallsManagerListener listener : mListeners) { 629 listener.onIsConferencedChanged(call); 630 } 631 } 632 633 @Override 634 public void onIsVoipAudioModeChanged(Call call) { 635 for (CallsManagerListener listener : mListeners) { 636 listener.onIsVoipAudioModeChanged(call); 637 } 638 } 639 640 @Override 641 public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) { 642 for (CallsManagerListener listener : mListeners) { 643 listener.onVideoStateChanged(call, previousVideoState, newVideoState); 644 } 645 } 646 647 @Override 648 public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call, 649 long disconnectionTimeout) { 650 mPendingCallsToDisconnect.add(call); 651 mHandler.postDelayed(new Runnable("CM.oCVNOCB", mLock) { 652 @Override 653 public void loggedRun() { 654 if (mPendingCallsToDisconnect.remove(call)) { 655 Log.i(this, "Delayed disconnection of call: %s", call); 656 call.disconnect(); 657 } 658 } 659 }.prepare(), disconnectionTimeout); 660 661 return true; 662 } 663 664 /** 665 * Handles changes to the {@link Connection.VideoProvider} for a call. Adds the 666 * {@link CallsManager} as a listener for the {@link VideoProviderProxy} which is created 667 * in {@link Call#setVideoProvider(IVideoProvider)}. This allows the {@link CallsManager} to 668 * respond to callbacks from the {@link VideoProviderProxy}. 669 * 670 * @param call The call. 671 */ 672 @Override 673 public void onVideoCallProviderChanged(Call call) { 674 VideoProviderProxy videoProviderProxy = call.getVideoProviderProxy(); 675 676 if (videoProviderProxy == null) { 677 return; 678 } 679 680 videoProviderProxy.addListener(this); 681 } 682 683 /** 684 * Handles session modification requests received via the {@link TelecomVideoCallCallback} for 685 * a call. Notifies listeners of the {@link CallsManager.CallsManagerListener} of the session 686 * modification request. 687 * 688 * @param call The call. 689 * @param videoProfile The {@link VideoProfile}. 690 */ 691 @Override 692 public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) { 693 int videoState = videoProfile != null ? videoProfile.getVideoState() : 694 VideoProfile.STATE_AUDIO_ONLY; 695 Log.v(TAG, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile 696 .videoStateToString(videoState)); 697 698 for (CallsManagerListener listener : mListeners) { 699 listener.onSessionModifyRequestReceived(call, videoProfile); 700 } 701 } 702 703 public Collection<Call> getCalls() { 704 return Collections.unmodifiableCollection(mCalls); 705 } 706 707 /** 708 * Play or stop a call hold tone for a call. Triggered via 709 * {@link Connection#sendConnectionEvent(String)} when the 710 * {@link Connection#EVENT_ON_HOLD_TONE_START} event or 711 * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the 712 * 713 * @param call The call which requested the hold tone. 714 */ 715 @Override 716 public void onHoldToneRequested(Call call) { 717 for (CallsManagerListener listener : mListeners) { 718 listener.onHoldToneRequested(call); 719 } 720 } 721 722 /** 723 * A {@link Call} managed by the {@link CallsManager} has requested a handover to another 724 * {@link PhoneAccount}. 725 * @param call The call. 726 * @param handoverTo The {@link PhoneAccountHandle} to handover the call to. 727 * @param videoState The desired video state of the call after handover. 728 * @param extras 729 */ 730 @Override 731 public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, 732 Bundle extras) { 733 requestHandover(call, handoverTo, videoState, extras); 734 } 735 736 @VisibleForTesting 737 public Call getForegroundCall() { 738 if (mCallAudioManager == null) { 739 // Happens when getForegroundCall is called before full initialization. 740 return null; 741 } 742 return mCallAudioManager.getForegroundCall(); 743 } 744 745 @Override 746 public UserHandle getCurrentUserHandle() { 747 return mCurrentUserHandle; 748 } 749 750 public CallAudioManager getCallAudioManager() { 751 return mCallAudioManager; 752 } 753 754 InCallController getInCallController() { 755 return mInCallController; 756 } 757 758 EmergencyCallHelper getEmergencyCallHelper() { 759 return mEmergencyCallHelper; 760 } 761 762 @VisibleForTesting 763 public boolean hasEmergencyCall() { 764 for (Call call : mCalls) { 765 if (call.isEmergencyCall()) { 766 return true; 767 } 768 } 769 return false; 770 } 771 772 boolean hasOnlyDisconnectedCalls() { 773 for (Call call : mCalls) { 774 if (!call.isDisconnected()) { 775 return false; 776 } 777 } 778 return true; 779 } 780 781 public boolean hasVideoCall() { 782 for (Call call : mCalls) { 783 if (VideoProfile.isVideo(call.getVideoState())) { 784 return true; 785 } 786 } 787 return false; 788 } 789 790 @VisibleForTesting 791 public CallAudioState getAudioState() { 792 return mCallAudioManager.getCallAudioState(); 793 } 794 795 boolean isTtySupported() { 796 return mTtyManager.isTtySupported(); 797 } 798 799 int getCurrentTtyMode() { 800 return mTtyManager.getCurrentTtyMode(); 801 } 802 803 @VisibleForTesting 804 public void addListener(CallsManagerListener listener) { 805 mListeners.add(listener); 806 } 807 808 void removeListener(CallsManagerListener listener) { 809 mListeners.remove(listener); 810 } 811 812 /** 813 * Starts the process to attach the call to a connection service. 814 * 815 * @param phoneAccountHandle The phone account which contains the component name of the 816 * connection service to use for this call. 817 * @param extras The optional extras Bundle passed with the intent used for the incoming call. 818 */ 819 void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 820 Log.d(this, "processIncomingCallIntent"); 821 boolean isHandover = extras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER); 822 Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS); 823 if (handle == null) { 824 // Required for backwards compatibility 825 handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER); 826 } 827 Call call = new Call( 828 getNextCallId(), 829 mContext, 830 this, 831 mLock, 832 mConnectionServiceRepository, 833 mContactsAsyncHelper, 834 mCallerInfoAsyncQueryFactory, 835 mPhoneNumberUtilsAdapter, 836 handle, 837 null /* gatewayInfo */, 838 null /* connectionManagerPhoneAccount */, 839 phoneAccountHandle, 840 Call.CALL_DIRECTION_INCOMING /* callDirection */, 841 false /* forceAttachToExistingConnection */, 842 false, /* isConference */ 843 mClockProxy); 844 845 // Ensure new calls related to self-managed calls/connections are set as such. This will 846 // be overridden when the actual connection is returned in startCreateConnection, however 847 // doing this now ensures the logs and any other logic will treat this call as self-managed 848 // from the moment it is created. 849 PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked( 850 phoneAccountHandle); 851 if (phoneAccount != null) { 852 call.setIsSelfManaged(phoneAccount.isSelfManaged()); 853 if (call.isSelfManaged()) { 854 // Self managed calls will always be voip audio mode. 855 call.setIsVoipAudioMode(true); 856 } else { 857 // Incoming call is not self-managed, so we need to set extras on it to indicate 858 // whether answering will cause a background self-managed call to drop. 859 if (hasSelfManagedCalls()) { 860 Bundle dropCallExtras = new Bundle(); 861 dropCallExtras.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true); 862 863 // Include the name of the app which will drop the call. 864 Call foregroundCall = getForegroundCall(); 865 if (foregroundCall != null) { 866 CharSequence droppedApp = foregroundCall.getTargetPhoneAccountLabel(); 867 dropCallExtras.putCharSequence( 868 Connection.EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME, droppedApp); 869 Log.i(this, "Incoming managed call will drop %s call.", droppedApp); 870 } 871 call.putExtras(Call.SOURCE_CONNECTION_SERVICE, dropCallExtras); 872 } 873 } 874 875 if (extras.getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) { 876 Log.d(this, "processIncomingCallIntent: defaulting to voip mode for call %s", 877 call.getId()); 878 call.setIsVoipAudioMode(true); 879 } 880 } 881 if (extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) { 882 if (phoneAccount != null && 883 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) { 884 call.setRttStreams(true); 885 } 886 } 887 // If the extras specifies a video state, set it on the call if the PhoneAccount supports 888 // video. 889 int videoState = VideoProfile.STATE_AUDIO_ONLY; 890 if (extras.containsKey(TelecomManager.EXTRA_INCOMING_VIDEO_STATE) && 891 phoneAccount != null && phoneAccount.hasCapabilities( 892 PhoneAccount.CAPABILITY_VIDEO_CALLING)) { 893 videoState = extras.getInt(TelecomManager.EXTRA_INCOMING_VIDEO_STATE); 894 call.setVideoState(videoState); 895 } 896 897 call.initAnalytics(); 898 if (getForegroundCall() != null) { 899 getForegroundCall().getAnalytics().setCallIsInterrupted(true); 900 call.getAnalytics().setCallIsAdditional(true); 901 } 902 setIntentExtrasAndStartTime(call, extras); 903 // TODO: Move this to be a part of addCall() 904 call.addListener(this); 905 906 boolean isHandoverAllowed = true; 907 if (isHandover) { 908 if (!isHandoverInProgress() && 909 isHandoverToPhoneAccountSupported(phoneAccountHandle)) { 910 final String handleScheme = handle.getSchemeSpecificPart(); 911 Call fromCall = mCalls.stream() 912 .filter((c) -> mPhoneNumberUtilsAdapter.isSamePhoneNumber( 913 c.getHandle().getSchemeSpecificPart(), handleScheme)) 914 .findFirst() 915 .orElse(null); 916 if (fromCall != null) { 917 if (!isHandoverFromPhoneAccountSupported(fromCall.getTargetPhoneAccount())) { 918 Log.w(this, "processIncomingCallIntent: From account doesn't support " + 919 "handover."); 920 isHandoverAllowed = false; 921 } 922 } else { 923 Log.w(this, "processIncomingCallIntent: handover fail; can't find from call."); 924 isHandoverAllowed = false; 925 } 926 927 if (isHandoverAllowed) { 928 // Link the calls so we know we're handing over. 929 fromCall.setHandoverDestinationCall(call); 930 call.setHandoverSourceCall(fromCall); 931 call.setHandoverState(HandoverState.HANDOVER_TO_STARTED); 932 fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); 933 Log.addEvent(fromCall, LogUtils.Events.START_HANDOVER, 934 "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId()); 935 Log.addEvent(call, LogUtils.Events.START_HANDOVER, 936 "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId()); 937 if (isSpeakerEnabledForVideoCalls() && VideoProfile.isVideo(videoState)) { 938 // Ensure when the call goes active that it will go to speakerphone if the 939 // handover to call is a video call. 940 call.setStartWithSpeakerphoneOn(true); 941 } 942 } 943 } else { 944 Log.w(this, "processIncomingCallIntent: To account doesn't support handover."); 945 } 946 } 947 948 if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call, 949 call.getTargetPhoneAccount()))) { 950 notifyCreateConnectionFailed(phoneAccountHandle, call); 951 } else { 952 call.startCreateConnection(mPhoneAccountRegistrar); 953 } 954 } 955 956 void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 957 Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE); 958 Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle)); 959 Call call = new Call( 960 getNextCallId(), 961 mContext, 962 this, 963 mLock, 964 mConnectionServiceRepository, 965 mContactsAsyncHelper, 966 mCallerInfoAsyncQueryFactory, 967 mPhoneNumberUtilsAdapter, 968 handle, 969 null /* gatewayInfo */, 970 null /* connectionManagerPhoneAccount */, 971 phoneAccountHandle, 972 Call.CALL_DIRECTION_UNKNOWN /* callDirection */, 973 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach 974 // to the existing connection instead of trying to create a new one. 975 true /* forceAttachToExistingConnection */, 976 false, /* isConference */ 977 mClockProxy); 978 call.initAnalytics(); 979 980 setIntentExtrasAndStartTime(call, extras); 981 call.addListener(this); 982 call.startCreateConnection(mPhoneAccountRegistrar); 983 } 984 985 private boolean areHandlesEqual(Uri handle1, Uri handle2) { 986 if (handle1 == null || handle2 == null) { 987 return handle1 == handle2; 988 } 989 990 if (!TextUtils.equals(handle1.getScheme(), handle2.getScheme())) { 991 return false; 992 } 993 994 final String number1 = PhoneNumberUtils.normalizeNumber(handle1.getSchemeSpecificPart()); 995 final String number2 = PhoneNumberUtils.normalizeNumber(handle2.getSchemeSpecificPart()); 996 return TextUtils.equals(number1, number2); 997 } 998 999 private Call reuseOutgoingCall(Uri handle) { 1000 // Check to see if we can reuse any of the calls that are waiting to disconnect. 1001 // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information. 1002 Call reusedCall = null; 1003 for (Iterator<Call> callIter = mPendingCallsToDisconnect.iterator(); callIter.hasNext();) { 1004 Call pendingCall = callIter.next(); 1005 if (reusedCall == null && areHandlesEqual(pendingCall.getHandle(), handle)) { 1006 callIter.remove(); 1007 Log.i(this, "Reusing disconnected call %s", pendingCall); 1008 reusedCall = pendingCall; 1009 } else { 1010 Log.i(this, "Not reusing disconnected call %s", pendingCall); 1011 callIter.remove(); 1012 pendingCall.disconnect(); 1013 } 1014 } 1015 1016 return reusedCall; 1017 } 1018 1019 /** 1020 * Kicks off the first steps to creating an outgoing call. 1021 * 1022 * For managed connections, this is the first step to launching the Incall UI. 1023 * For self-managed connections, we don't expect the Incall UI to launch, but this is still a 1024 * first step in getting the self-managed ConnectionService to create the connection. 1025 * @param handle Handle to connect the call with. 1026 * @param phoneAccountHandle The phone account which contains the component name of the 1027 * connection service to use for this call. 1028 * @param extras The optional extras Bundle passed with the intent used for the incoming call. 1029 * @param initiatingUser {@link UserHandle} of user that place the outgoing call. 1030 * @param originalIntent 1031 */ 1032 Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras, 1033 UserHandle initiatingUser, Intent originalIntent) { 1034 boolean isReusedCall = true; 1035 Call call = reuseOutgoingCall(handle); 1036 1037 PhoneAccount account = 1038 mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser); 1039 boolean isSelfManaged = account != null && account.isSelfManaged(); 1040 1041 // Create a call with original handle. The handle may be changed when the call is attached 1042 // to a connection service, but in most cases will remain the same. 1043 if (call == null) { 1044 call = new Call(getNextCallId(), mContext, 1045 this, 1046 mLock, 1047 mConnectionServiceRepository, 1048 mContactsAsyncHelper, 1049 mCallerInfoAsyncQueryFactory, 1050 mPhoneNumberUtilsAdapter, 1051 handle, 1052 null /* gatewayInfo */, 1053 null /* connectionManagerPhoneAccount */, 1054 null /* phoneAccountHandle */, 1055 Call.CALL_DIRECTION_OUTGOING /* callDirection */, 1056 false /* forceAttachToExistingConnection */, 1057 false, /* isConference */ 1058 mClockProxy); 1059 call.initAnalytics(); 1060 1061 // Ensure new calls related to self-managed calls/connections are set as such. This 1062 // will be overridden when the actual connection is returned in startCreateConnection, 1063 // however doing this now ensures the logs and any other logic will treat this call as 1064 // self-managed from the moment it is created. 1065 call.setIsSelfManaged(isSelfManaged); 1066 if (isSelfManaged) { 1067 // Self-managed calls will ALWAYS use voip audio mode. 1068 call.setIsVoipAudioMode(true); 1069 } 1070 call.setInitiatingUser(initiatingUser); 1071 isReusedCall = false; 1072 } 1073 1074 int videoState = VideoProfile.STATE_AUDIO_ONLY; 1075 if (extras != null) { 1076 // Set the video state on the call early so that when it is added to the InCall UI the 1077 // UI knows to configure itself as a video call immediately. 1078 videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 1079 VideoProfile.STATE_AUDIO_ONLY); 1080 1081 // If this is an emergency video call, we need to check if the phone account supports 1082 // emergency video calling. 1083 // Also, ensure we don't try to place an outgoing call with video if video is not 1084 // supported. 1085 if (VideoProfile.isVideo(videoState)) { 1086 if (call.isEmergencyCall() && account != null && 1087 !account.hasCapabilities(PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING)) { 1088 // Phone account doesn't support emergency video calling, so fallback to 1089 // audio-only now to prevent the InCall UI from setting up video surfaces 1090 // needlessly. 1091 Log.i(this, "startOutgoingCall - emergency video calls not supported; " + 1092 "falling back to audio-only"); 1093 videoState = VideoProfile.STATE_AUDIO_ONLY; 1094 } else if (account != null && 1095 !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) { 1096 // Phone account doesn't support video calling, so fallback to audio-only. 1097 Log.i(this, "startOutgoingCall - video calls not supported; fallback to " + 1098 "audio-only."); 1099 videoState = VideoProfile.STATE_AUDIO_ONLY; 1100 } 1101 } 1102 1103 call.setVideoState(videoState); 1104 } 1105 1106 List<PhoneAccountHandle> potentialPhoneAccounts = findOutgoingCallPhoneAccount( 1107 phoneAccountHandle, handle, VideoProfile.isVideo(videoState), initiatingUser); 1108 if (potentialPhoneAccounts.size() == 1) { 1109 phoneAccountHandle = potentialPhoneAccounts.get(0); 1110 } else { 1111 phoneAccountHandle = null; 1112 } 1113 call.setTargetPhoneAccount(phoneAccountHandle); 1114 1115 boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle) && !isSelfManaged; 1116 1117 // Do not support any more live calls. Our options are to move a call to hold, disconnect 1118 // a call, or cancel this call altogether. If a call is being reused, then it has already 1119 // passed the makeRoomForOutgoingCall check once and will fail the second time due to the 1120 // call transitioning into the CONNECTING state. 1121 if (!isSelfManaged && !isPotentialInCallMMICode && (!isReusedCall && 1122 !makeRoomForOutgoingCall(call, call.isEmergencyCall()))) { 1123 // just cancel at this point. 1124 Log.i(this, "No remaining room for outgoing call: %s", call); 1125 if (mCalls.contains(call)) { 1126 // This call can already exist if it is a reused call, 1127 // See {@link #reuseOutgoingCall}. 1128 call.disconnect(); 1129 } 1130 return null; 1131 } 1132 1133 boolean needsAccountSelection = 1134 phoneAccountHandle == null && potentialPhoneAccounts.size() > 1 1135 && !call.isEmergencyCall() && !isSelfManaged; 1136 1137 if (needsAccountSelection) { 1138 // This is the state where the user is expected to select an account 1139 call.setState(CallState.SELECT_PHONE_ACCOUNT, "needs account selection"); 1140 // Create our own instance to modify (since extras may be Bundle.EMPTY) 1141 extras = new Bundle(extras); 1142 extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, 1143 potentialPhoneAccounts); 1144 } else { 1145 PhoneAccount accountToUse = 1146 mPhoneAccountRegistrar.getPhoneAccount(phoneAccountHandle, initiatingUser); 1147 if (accountToUse != null && accountToUse.getExtras() != null) { 1148 if (accountToUse.getExtras() 1149 .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) { 1150 Log.d(this, "startOutgoingCall: defaulting to voip mode for call %s", 1151 call.getId()); 1152 call.setIsVoipAudioMode(true); 1153 } 1154 } 1155 1156 call.setState( 1157 CallState.CONNECTING, 1158 phoneAccountHandle == null ? "no-handle" : phoneAccountHandle.toString()); 1159 if (extras != null 1160 && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) { 1161 if (accountToUse != null 1162 && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) { 1163 call.setRttStreams(true); 1164 } 1165 } 1166 } 1167 setIntentExtrasAndStartTime(call, extras); 1168 1169 if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) 1170 && !needsAccountSelection) { 1171 // Do not add the call if it is a potential MMI code. 1172 call.addListener(this); 1173 } else if (!isSelfManaged && hasSelfManagedCalls() && !call.isEmergencyCall()) { 1174 // Adding a managed call and there are ongoing self-managed call(s). 1175 call.setOriginalCallIntent(originalIntent); 1176 startCallConfirmation(call); 1177 return null; 1178 } else if (!mCalls.contains(call)) { 1179 // We check if mCalls already contains the call because we could potentially be reusing 1180 // a call which was previously added (See {@link #reuseOutgoingCall}). 1181 addCall(call); 1182 } 1183 1184 return call; 1185 } 1186 1187 /** 1188 * Finds the {@link PhoneAccountHandle}(s) which could potentially be used to place an outgoing 1189 * call. Takes into account the following: 1190 * 1. Any pre-chosen {@link PhoneAccountHandle} which was specified on the 1191 * {@link Intent#ACTION_CALL} intent. If one was chosen it will be used if possible. 1192 * 2. Whether the call is a video call. If the call being placed is a video call, an attempt is 1193 * first made to consider video capable phone accounts. If no video capable phone accounts are 1194 * found, the usual non-video capable phone accounts will be considered. 1195 * 3. Whether there is a user-chosen default phone account; that one will be used if possible. 1196 * 1197 * @param targetPhoneAccountHandle The pre-chosen {@link PhoneAccountHandle} passed in when the 1198 * call was placed. Will be {@code null} if the 1199 * {@link Intent#ACTION_CALL} intent did not specify a target 1200 * phone account. 1201 * @param handle The handle of the outgoing call; used to determine the SIP scheme when matching 1202 * phone accounts. 1203 * @param isVideo {@code true} if the call is a video call, {@code false} otherwise. 1204 * @param initiatingUser The {@link UserHandle} the call is placed on. 1205 * @return 1206 */ 1207 @VisibleForTesting 1208 public List<PhoneAccountHandle> findOutgoingCallPhoneAccount( 1209 PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo, 1210 UserHandle initiatingUser) { 1211 boolean isSelfManaged = isSelfManaged(targetPhoneAccountHandle, initiatingUser); 1212 1213 List<PhoneAccountHandle> accounts; 1214 if (!isSelfManaged) { 1215 // Try to find a potential phone account, taking into account whether this is a video 1216 // call. 1217 accounts = constructPossiblePhoneAccounts(handle, initiatingUser, isVideo); 1218 if (isVideo && accounts.size() == 0) { 1219 // Placing a video call but no video capable accounts were found, so consider any 1220 // call capable accounts (we can fallback to audio). 1221 accounts = constructPossiblePhoneAccounts(handle, initiatingUser, 1222 false /* isVideo */); 1223 } 1224 Log.v(this, "findOutgoingCallPhoneAccount: accounts = " + accounts); 1225 1226 // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this 1227 // call as if a phoneAccount was not specified (does the default behavior instead). 1228 // Note: We will not attempt to dial with a requested phoneAccount if it is disabled. 1229 if (targetPhoneAccountHandle != null) { 1230 if (!accounts.contains(targetPhoneAccountHandle)) { 1231 targetPhoneAccountHandle = null; 1232 } else { 1233 // The target phone account is valid and was found. 1234 return Arrays.asList(targetPhoneAccountHandle); 1235 } 1236 } 1237 1238 if (targetPhoneAccountHandle == null && accounts.size() > 0) { 1239 // No preset account, check if default exists that supports the URI scheme for the 1240 // handle and verify it can be used. 1241 if (accounts.size() > 1) { 1242 PhoneAccountHandle defaultPhoneAccountHandle = 1243 mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme( 1244 handle.getScheme(), initiatingUser); 1245 if (defaultPhoneAccountHandle != null && 1246 accounts.contains(defaultPhoneAccountHandle)) { 1247 accounts.clear(); 1248 accounts.add(defaultPhoneAccountHandle); 1249 } 1250 } 1251 } 1252 } else { 1253 // Self-managed ConnectionServices can only have a single potential account. 1254 accounts = Arrays.asList(targetPhoneAccountHandle); 1255 } 1256 return accounts; 1257 } 1258 1259 private boolean isSelfManaged(PhoneAccountHandle targetPhoneAccountHandle, 1260 UserHandle initiatingUser) { 1261 PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount( 1262 targetPhoneAccountHandle, initiatingUser); 1263 return targetPhoneAccount != null && targetPhoneAccount.isSelfManaged(); 1264 } 1265 1266 /** 1267 * Attempts to issue/connect the specified call. 1268 * 1269 * @param handle Handle to connect the call with. 1270 * @param gatewayInfo Optional gateway information that can be used to route the call to the 1271 * actual dialed handle via a gateway provider. May be null. 1272 * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects. 1273 * @param videoState The desired video state for the outgoing call. 1274 */ 1275 @VisibleForTesting 1276 public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, 1277 boolean speakerphoneOn, int videoState) { 1278 if (call == null) { 1279 // don't do anything if the call no longer exists 1280 Log.i(this, "Canceling unknown call."); 1281 return; 1282 } 1283 1284 final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress(); 1285 1286 if (gatewayInfo == null) { 1287 Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle)); 1288 } else { 1289 Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s", 1290 Log.pii(uriHandle), Log.pii(handle)); 1291 } 1292 1293 call.setHandle(uriHandle); 1294 call.setGatewayInfo(gatewayInfo); 1295 1296 final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean( 1297 R.bool.use_speaker_when_docked); 1298 final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock(); 1299 final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState); 1300 1301 // Auto-enable speakerphone if the originating intent specified to do so, if the call 1302 // is a video call, of if using speaker when docked 1303 call.setStartWithSpeakerphoneOn(speakerphoneOn || useSpeakerForVideoCall 1304 || (useSpeakerWhenDocked && useSpeakerForDock)); 1305 call.setVideoState(videoState); 1306 1307 if (speakerphoneOn) { 1308 Log.i(this, "%s Starting with speakerphone as requested", call); 1309 } else if (useSpeakerWhenDocked && useSpeakerForDock) { 1310 Log.i(this, "%s Starting with speakerphone because car is docked.", call); 1311 } else if (useSpeakerForVideoCall) { 1312 Log.i(this, "%s Starting with speakerphone because its a video call.", call); 1313 } 1314 1315 if (call.isEmergencyCall()) { 1316 new AsyncEmergencyContactNotifier(mContext).execute(); 1317 } 1318 1319 final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean( 1320 com.android.internal.R.bool.config_requireCallCapableAccountForHandle); 1321 final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call, 1322 call.getTargetPhoneAccount()); 1323 if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) { 1324 // If the account has been set, proceed to place the outgoing call. 1325 // Otherwise the connection will be initiated when the account is set by the user. 1326 if (call.isSelfManaged() && !isOutgoingCallPermitted) { 1327 notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call); 1328 } else if (!call.isSelfManaged() && hasSelfManagedCalls() && !call.isEmergencyCall()) { 1329 markCallDisconnectedDueToSelfManagedCall(call); 1330 } else { 1331 if (call.isEmergencyCall()) { 1332 // Disconnect all self-managed calls to make priority for emergency call. 1333 disconnectSelfManagedCalls(); 1334 } 1335 1336 call.startCreateConnection(mPhoneAccountRegistrar); 1337 } 1338 } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts( 1339 requireCallCapableAccountByHandle ? call.getHandle().getScheme() : null, false, 1340 call.getInitiatingUser()).isEmpty()) { 1341 // If there are no call capable accounts, disconnect the call. 1342 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED, 1343 "No registered PhoneAccounts")); 1344 markCallAsRemoved(call); 1345 } 1346 } 1347 1348 /** 1349 * Attempts to start a conference call for the specified call. 1350 * 1351 * @param call The call to conference. 1352 * @param otherCall The other call to conference with. 1353 */ 1354 @VisibleForTesting 1355 public void conference(Call call, Call otherCall) { 1356 call.conferenceWith(otherCall); 1357 } 1358 1359 /** 1360 * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call 1361 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 1362 * the user opting to answer said call. 1363 * 1364 * @param call The call to answer. 1365 * @param videoState The video state in which to answer the call. 1366 */ 1367 @VisibleForTesting 1368 public void answerCall(Call call, int videoState) { 1369 if (!mCalls.contains(call)) { 1370 Log.i(this, "Request to answer a non-existent call %s", call); 1371 } else { 1372 Call foregroundCall = getForegroundCall(); 1373 // If the foreground call is not the ringing call and it is currently isActive() or 1374 // STATE_DIALING, put it on hold before answering the call. 1375 if (foregroundCall != null && foregroundCall != call && 1376 (foregroundCall.isActive() || 1377 foregroundCall.getState() == CallState.DIALING || 1378 foregroundCall.getState() == CallState.PULLING)) { 1379 if (!foregroundCall.getTargetPhoneAccount().equals( 1380 call.getTargetPhoneAccount()) && 1381 ((call.isSelfManaged() != foregroundCall.isSelfManaged()) || 1382 call.isSelfManaged())) { 1383 // The foreground call is from another connection service, and either: 1384 // 1. FG call's managed state doesn't match that of the incoming call. 1385 // E.g. Incoming is self-managed and FG is managed, or incoming is managed 1386 // and foreground is self-managed. 1387 // 2. The incoming call is self-managed. 1388 // E.g. The incoming call is 1389 Log.i(this, "Answering call from %s CS; disconnecting calls from %s CS.", 1390 foregroundCall.isSelfManaged() ? "selfMg" : "mg", 1391 call.isSelfManaged() ? "selfMg" : "mg"); 1392 disconnectOtherCalls(call.getTargetPhoneAccount()); 1393 } else if (0 == (foregroundCall.getConnectionCapabilities() 1394 & Connection.CAPABILITY_HOLD)) { 1395 // This call does not support hold. If it is from a different connection 1396 // service, then disconnect it, otherwise allow the connection service to 1397 // figure out the right states. 1398 if (foregroundCall.getConnectionService() != call.getConnectionService()) { 1399 foregroundCall.disconnect(); 1400 } 1401 } else { 1402 Call heldCall = getHeldCall(); 1403 if (heldCall != null) { 1404 Log.i(this, "Disconnecting held call %s before holding active call.", 1405 heldCall); 1406 heldCall.disconnect(); 1407 } 1408 1409 foregroundCall.hold(); 1410 } 1411 // TODO: Wait until we get confirmation of the active call being 1412 // on-hold before answering the new call. 1413 // TODO: Import logic from CallManager.acceptCall() 1414 } 1415 1416 for (CallsManagerListener listener : mListeners) { 1417 listener.onIncomingCallAnswered(call); 1418 } 1419 1420 // We do not update the UI until we get confirmation of the answer() through 1421 // {@link #markCallAsActive}. 1422 call.answer(videoState); 1423 if (isSpeakerphoneAutoEnabledForVideoCalls(videoState)) { 1424 call.setStartWithSpeakerphoneOn(true); 1425 } 1426 } 1427 } 1428 1429 /** 1430 * Determines if the speakerphone should be automatically enabled for the call. Speakerphone 1431 * should be enabled if the call is a video call and bluetooth or the wired headset are not in 1432 * use. 1433 * 1434 * @param videoState The video state of the call. 1435 * @return {@code true} if the speakerphone should be enabled. 1436 */ 1437 public boolean isSpeakerphoneAutoEnabledForVideoCalls(int videoState) { 1438 return VideoProfile.isVideo(videoState) && 1439 !mWiredHeadsetManager.isPluggedIn() && 1440 !mBluetoothRouteManager.isBluetoothAvailable() && 1441 isSpeakerEnabledForVideoCalls(); 1442 } 1443 1444 /** 1445 * Determines if the speakerphone should be enabled for when docked. Speakerphone 1446 * should be enabled if the device is docked and bluetooth or the wired headset are 1447 * not in use. 1448 * 1449 * @return {@code true} if the speakerphone should be enabled for the dock. 1450 */ 1451 private boolean isSpeakerphoneEnabledForDock() { 1452 return mDockManager.isDocked() && 1453 !mWiredHeadsetManager.isPluggedIn() && 1454 !mBluetoothRouteManager.isBluetoothAvailable(); 1455 } 1456 1457 /** 1458 * Determines if the speakerphone should be automatically enabled for video calls. 1459 * 1460 * @return {@code true} if the speakerphone should automatically be enabled. 1461 */ 1462 private static boolean isSpeakerEnabledForVideoCalls() { 1463 return (SystemProperties.getInt(TelephonyProperties.PROPERTY_VIDEOCALL_AUDIO_OUTPUT, 1464 PhoneConstants.AUDIO_OUTPUT_DEFAULT) == 1465 PhoneConstants.AUDIO_OUTPUT_ENABLE_SPEAKER); 1466 } 1467 1468 /** 1469 * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call 1470 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 1471 * the user opting to reject said call. 1472 */ 1473 @VisibleForTesting 1474 public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) { 1475 if (!mCalls.contains(call)) { 1476 Log.i(this, "Request to reject a non-existent call %s", call); 1477 } else { 1478 for (CallsManagerListener listener : mListeners) { 1479 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage); 1480 } 1481 call.reject(rejectWithMessage, textMessage); 1482 } 1483 } 1484 1485 /** 1486 * Instructs Telecom to play the specified DTMF tone within the specified call. 1487 * 1488 * @param digit The DTMF digit to play. 1489 */ 1490 @VisibleForTesting 1491 public void playDtmfTone(Call call, char digit) { 1492 if (!mCalls.contains(call)) { 1493 Log.i(this, "Request to play DTMF in a non-existent call %s", call); 1494 } else { 1495 call.playDtmfTone(digit); 1496 mDtmfLocalTonePlayer.playTone(call, digit); 1497 } 1498 } 1499 1500 /** 1501 * Instructs Telecom to stop the currently playing DTMF tone, if any. 1502 */ 1503 @VisibleForTesting 1504 public void stopDtmfTone(Call call) { 1505 if (!mCalls.contains(call)) { 1506 Log.i(this, "Request to stop DTMF in a non-existent call %s", call); 1507 } else { 1508 call.stopDtmfTone(); 1509 mDtmfLocalTonePlayer.stopTone(call); 1510 } 1511 } 1512 1513 /** 1514 * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any. 1515 */ 1516 void postDialContinue(Call call, boolean proceed) { 1517 if (!mCalls.contains(call)) { 1518 Log.i(this, "Request to continue post-dial string in a non-existent call %s", call); 1519 } else { 1520 call.postDialContinue(proceed); 1521 } 1522 } 1523 1524 /** 1525 * Instructs Telecom to disconnect the specified call. Intended to be invoked by the 1526 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 1527 * the user hitting the end-call button. 1528 */ 1529 @VisibleForTesting 1530 public void disconnectCall(Call call) { 1531 Log.v(this, "disconnectCall %s", call); 1532 1533 if (!mCalls.contains(call)) { 1534 Log.w(this, "Unknown call (%s) asked to disconnect", call); 1535 } else { 1536 mLocallyDisconnectingCalls.add(call); 1537 call.disconnect(); 1538 } 1539 } 1540 1541 /** 1542 * Instructs Telecom to disconnect all calls. 1543 */ 1544 void disconnectAllCalls() { 1545 Log.v(this, "disconnectAllCalls"); 1546 1547 for (Call call : mCalls) { 1548 disconnectCall(call); 1549 } 1550 } 1551 1552 /** 1553 * Disconnects calls for any other {@link PhoneAccountHandle} but the one specified. 1554 * Note: As a protective measure, will NEVER disconnect an emergency call. Although that 1555 * situation should never arise, its a good safeguard. 1556 * @param phoneAccountHandle Calls owned by {@link PhoneAccountHandle}s other than this one will 1557 * be disconnected. 1558 */ 1559 private void disconnectOtherCalls(PhoneAccountHandle phoneAccountHandle) { 1560 mCalls.stream() 1561 .filter(c -> !c.isEmergencyCall() && 1562 !c.getTargetPhoneAccount().equals(phoneAccountHandle)) 1563 .forEach(c -> disconnectCall(c)); 1564 } 1565 1566 /** 1567 * Instructs Telecom to put the specified call on hold. Intended to be invoked by the 1568 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 1569 * the user hitting the hold button during an active call. 1570 */ 1571 @VisibleForTesting 1572 public void holdCall(Call call) { 1573 if (!mCalls.contains(call)) { 1574 Log.w(this, "Unknown call (%s) asked to be put on hold", call); 1575 } else { 1576 Log.d(this, "Putting call on hold: (%s)", call); 1577 call.hold(); 1578 } 1579 } 1580 1581 /** 1582 * Instructs Telecom to release the specified call from hold. Intended to be invoked by 1583 * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered 1584 * by the user hitting the hold button during a held call. 1585 */ 1586 @VisibleForTesting 1587 public void unholdCall(Call call) { 1588 if (!mCalls.contains(call)) { 1589 Log.w(this, "Unknown call (%s) asked to be removed from hold", call); 1590 } else { 1591 boolean otherCallHeld = false; 1592 Log.d(this, "unholding call: (%s)", call); 1593 for (Call c : mCalls) { 1594 // Only attempt to hold parent calls and not the individual children. 1595 if (c != null && c.isAlive() && c != call && c.getParentCall() == null) { 1596 otherCallHeld = true; 1597 Log.addEvent(c, LogUtils.Events.SWAP); 1598 c.hold(); 1599 } 1600 } 1601 if (otherCallHeld) { 1602 Log.addEvent(call, LogUtils.Events.SWAP); 1603 } 1604 call.unhold(); 1605 } 1606 } 1607 1608 @Override 1609 public void onExtrasChanged(Call c, int source, Bundle extras) { 1610 if (source != Call.SOURCE_CONNECTION_SERVICE) { 1611 return; 1612 } 1613 handleCallTechnologyChange(c); 1614 handleChildAddressChange(c); 1615 updateCanAddCall(); 1616 } 1617 1618 // Construct the list of possible PhoneAccounts that the outgoing call can use based on the 1619 // active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount, 1620 // then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP. 1621 @VisibleForTesting 1622 public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user, 1623 boolean isVideo) { 1624 if (handle == null) { 1625 return Collections.emptyList(); 1626 } 1627 // If we're specifically looking for video capable accounts, then include that capability, 1628 // otherwise specify no additional capability constraints. 1629 List<PhoneAccountHandle> allAccounts = 1630 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user, 1631 isVideo ? PhoneAccount.CAPABILITY_VIDEO_CALLING : 0 /* any */); 1632 // First check the Radio SIM Technology 1633 if(mRadioSimVariants == null) { 1634 TelephonyManager tm = (TelephonyManager) mContext.getSystemService( 1635 Context.TELEPHONY_SERVICE); 1636 // Cache Sim Variants 1637 mRadioSimVariants = tm.getMultiSimConfiguration(); 1638 } 1639 // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount 1640 // Should be available if a call is already active on the SIM account. 1641 if(mRadioSimVariants != TelephonyManager.MultiSimVariants.DSDA) { 1642 List<PhoneAccountHandle> simAccounts = 1643 mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser(); 1644 PhoneAccountHandle ongoingCallAccount = null; 1645 for (Call c : mCalls) { 1646 if (!c.isDisconnected() && !c.isNew() && simAccounts.contains( 1647 c.getTargetPhoneAccount())) { 1648 ongoingCallAccount = c.getTargetPhoneAccount(); 1649 break; 1650 } 1651 } 1652 if (ongoingCallAccount != null) { 1653 // Remove all SIM accounts that are not the active SIM from the list. 1654 simAccounts.remove(ongoingCallAccount); 1655 allAccounts.removeAll(simAccounts); 1656 } 1657 } 1658 return allAccounts; 1659 } 1660 1661 /** 1662 * Informs listeners (notably {@link CallAudioManager} of a change to the call's external 1663 * property. 1664 * . 1665 * @param call The call whose external property changed. 1666 * @param isExternalCall {@code True} if the call is now external, {@code false} otherwise. 1667 */ 1668 @Override 1669 public void onExternalCallChanged(Call call, boolean isExternalCall) { 1670 Log.v(this, "onConnectionPropertiesChanged: %b", isExternalCall); 1671 for (CallsManagerListener listener : mListeners) { 1672 listener.onExternalCallChanged(call, isExternalCall); 1673 } 1674 } 1675 1676 private void handleCallTechnologyChange(Call call) { 1677 if (call.getExtras() != null 1678 && call.getExtras().containsKey(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE)) { 1679 1680 Integer analyticsCallTechnology = sAnalyticsTechnologyMap.get( 1681 call.getExtras().getInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE)); 1682 if (analyticsCallTechnology == null) { 1683 analyticsCallTechnology = Analytics.THIRD_PARTY_PHONE; 1684 } 1685 call.getAnalytics().addCallTechnology(analyticsCallTechnology); 1686 } 1687 } 1688 1689 public void handleChildAddressChange(Call call) { 1690 if (call.getExtras() != null 1691 && call.getExtras().containsKey(Connection.EXTRA_CHILD_ADDRESS)) { 1692 1693 String viaNumber = call.getExtras().getString(Connection.EXTRA_CHILD_ADDRESS); 1694 call.setViaNumber(viaNumber); 1695 } 1696 } 1697 1698 /** Called by the in-call UI to change the mute state. */ 1699 void mute(boolean shouldMute) { 1700 mCallAudioManager.mute(shouldMute); 1701 } 1702 1703 /** 1704 * Called by the in-call UI to change the audio route, for example to change from earpiece to 1705 * speaker phone. 1706 */ 1707 void setAudioRoute(int route, String bluetoothAddress) { 1708 mCallAudioManager.setAudioRoute(route, bluetoothAddress); 1709 } 1710 1711 /** Called by the in-call UI to turn the proximity sensor on. */ 1712 void turnOnProximitySensor() { 1713 mProximitySensorManager.turnOn(); 1714 } 1715 1716 /** 1717 * Called by the in-call UI to turn the proximity sensor off. 1718 * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise, 1719 * the screen will be kept off until the proximity sensor goes negative. 1720 */ 1721 void turnOffProximitySensor(boolean screenOnImmediately) { 1722 mProximitySensorManager.turnOff(screenOnImmediately); 1723 } 1724 1725 void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) { 1726 if (!mCalls.contains(call)) { 1727 Log.i(this, "Attempted to add account to unknown call %s", call); 1728 } else { 1729 call.setTargetPhoneAccount(account); 1730 PhoneAccount realPhoneAccount = 1731 mPhoneAccountRegistrar.getPhoneAccountUnchecked(account); 1732 if (realPhoneAccount != null && realPhoneAccount.getExtras() != null 1733 && realPhoneAccount.getExtras() 1734 .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) { 1735 Log.d("phoneAccountSelected: default to voip mode for call %s", call.getId()); 1736 call.setIsVoipAudioMode(true); 1737 } 1738 if (call.getIntentExtras() 1739 .getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) { 1740 if (realPhoneAccount != null 1741 && realPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT)) { 1742 call.setRttStreams(true); 1743 } 1744 } 1745 1746 if (!call.isNewOutgoingCallIntentBroadcastDone()) { 1747 return; 1748 } 1749 1750 // Note: emergency calls never go through account selection dialog so they never 1751 // arrive here. 1752 if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) { 1753 call.startCreateConnection(mPhoneAccountRegistrar); 1754 } else { 1755 call.disconnect(); 1756 } 1757 1758 if (setDefault) { 1759 mPhoneAccountRegistrar 1760 .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser()); 1761 } 1762 } 1763 } 1764 1765 /** Called when the audio state changes. */ 1766 @VisibleForTesting 1767 public void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState 1768 newAudioState) { 1769 Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState); 1770 for (CallsManagerListener listener : mListeners) { 1771 listener.onCallAudioStateChanged(oldAudioState, newAudioState); 1772 } 1773 } 1774 1775 void markCallAsRinging(Call call) { 1776 setCallState(call, CallState.RINGING, "ringing set explicitly"); 1777 } 1778 1779 void markCallAsDialing(Call call) { 1780 setCallState(call, CallState.DIALING, "dialing set explicitly"); 1781 maybeMoveToSpeakerPhone(call); 1782 } 1783 1784 void markCallAsPulling(Call call) { 1785 setCallState(call, CallState.PULLING, "pulling set explicitly"); 1786 maybeMoveToSpeakerPhone(call); 1787 } 1788 1789 void markCallAsActive(Call call) { 1790 setCallState(call, CallState.ACTIVE, "active set explicitly"); 1791 maybeMoveToSpeakerPhone(call); 1792 } 1793 1794 void markCallAsOnHold(Call call) { 1795 setCallState(call, CallState.ON_HOLD, "on-hold set explicitly"); 1796 } 1797 1798 /** 1799 * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the 1800 * last live call, then also disconnect from the in-call controller. 1801 * 1802 * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}. 1803 */ 1804 void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) { 1805 call.setDisconnectCause(disconnectCause); 1806 setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly"); 1807 } 1808 1809 /** 1810 * Removes an existing disconnected call, and notifies the in-call app. 1811 */ 1812 void markCallAsRemoved(Call call) { 1813 call.maybeCleanupHandover(); 1814 removeCall(call); 1815 Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall(); 1816 if (mLocallyDisconnectingCalls.contains(call)) { 1817 boolean isDisconnectingChildCall = call.isDisconnectingChildCall(); 1818 Log.v(this, "markCallAsRemoved: isDisconnectingChildCall = " 1819 + isDisconnectingChildCall + "call -> %s", call); 1820 mLocallyDisconnectingCalls.remove(call); 1821 // Auto-unhold the foreground call due to a locally disconnected call, except if the 1822 // call which was disconnected is a member of a conference (don't want to auto un-hold 1823 // the conference if we remove a member of the conference). 1824 if (!isDisconnectingChildCall && foregroundCall != null 1825 && foregroundCall.getState() == CallState.ON_HOLD) { 1826 foregroundCall.unhold(); 1827 } 1828 } else if (foregroundCall != null && 1829 !foregroundCall.can(Connection.CAPABILITY_SUPPORT_HOLD) && 1830 foregroundCall.getState() == CallState.ON_HOLD) { 1831 1832 // The new foreground call is on hold, however the carrier does not display the hold 1833 // button in the UI. Therefore, we need to auto unhold the held call since the user has 1834 // no means of unholding it themselves. 1835 Log.i(this, "Auto-unholding held foreground call (call doesn't support hold)"); 1836 foregroundCall.unhold(); 1837 } 1838 } 1839 1840 /** 1841 * Given a call, marks the call as disconnected and removes it. Set the error message to 1842 * indicate to the user that the call cannot me placed due to an ongoing call in another app. 1843 * 1844 * Used when there are ongoing self-managed calls and the user tries to make an outgoing managed 1845 * call. Called by {@link #startCallConfirmation(Call)} when the user is already confirming an 1846 * outgoing call. Realistically this should almost never be called since in practice the user 1847 * won't make multiple outgoing calls at the same time. 1848 * 1849 * @param call The call to mark as disconnected. 1850 */ 1851 void markCallDisconnectedDueToSelfManagedCall(Call call) { 1852 Call activeCall = getActiveCall(); 1853 CharSequence errorMessage; 1854 if (activeCall == null) { 1855 // Realistically this shouldn't happen, but best to handle gracefully 1856 errorMessage = mContext.getText(R.string.cant_call_due_to_ongoing_unknown_call); 1857 } else { 1858 errorMessage = mContext.getString(R.string.cant_call_due_to_ongoing_call, 1859 activeCall.getTargetPhoneAccountLabel()); 1860 } 1861 // Call is managed and there are ongoing self-managed calls. 1862 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR, 1863 errorMessage, errorMessage, "Ongoing call in another app.")); 1864 markCallAsRemoved(call); 1865 } 1866 1867 /** 1868 * Cleans up any calls currently associated with the specified connection service when the 1869 * service binder disconnects unexpectedly. 1870 * 1871 * @param service The connection service that disconnected. 1872 */ 1873 void handleConnectionServiceDeath(ConnectionServiceWrapper service) { 1874 if (service != null) { 1875 Log.i(this, "handleConnectionServiceDeath: service %s died", service); 1876 for (Call call : mCalls) { 1877 if (call.getConnectionService() == service) { 1878 if (call.getState() != CallState.DISCONNECTED) { 1879 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR, 1880 "CS_DEATH")); 1881 } 1882 markCallAsRemoved(call); 1883 } 1884 } 1885 } 1886 } 1887 1888 /** 1889 * Determines if the {@link CallsManager} has any non-external calls. 1890 * 1891 * @return {@code True} if there are any non-external calls, {@code false} otherwise. 1892 */ 1893 boolean hasAnyCalls() { 1894 if (mCalls.isEmpty()) { 1895 return false; 1896 } 1897 1898 for (Call call : mCalls) { 1899 if (!call.isExternalCall()) { 1900 return true; 1901 } 1902 } 1903 return false; 1904 } 1905 1906 boolean hasActiveOrHoldingCall() { 1907 return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null; 1908 } 1909 1910 boolean hasRingingCall() { 1911 return getFirstCallWithState(CallState.RINGING) != null; 1912 } 1913 1914 boolean onMediaButton(int type) { 1915 if (hasAnyCalls()) { 1916 Call ringingCall = getFirstCallWithState(CallState.RINGING); 1917 if (HeadsetMediaButton.SHORT_PRESS == type) { 1918 if (ringingCall == null) { 1919 Call callToHangup = getFirstCallWithState(CallState.RINGING, CallState.DIALING, 1920 CallState.PULLING, CallState.ACTIVE, CallState.ON_HOLD); 1921 Log.addEvent(callToHangup, LogUtils.Events.INFO, 1922 "media btn short press - end call."); 1923 if (callToHangup != null) { 1924 disconnectCall(callToHangup); 1925 return true; 1926 } 1927 } else { 1928 ringingCall.answer(VideoProfile.STATE_AUDIO_ONLY); 1929 return true; 1930 } 1931 } else if (HeadsetMediaButton.LONG_PRESS == type) { 1932 if (ringingCall != null) { 1933 Log.addEvent(getForegroundCall(), 1934 LogUtils.Events.INFO, "media btn long press - reject"); 1935 ringingCall.reject(false, null); 1936 } else { 1937 Log.addEvent(getForegroundCall(), LogUtils.Events.INFO, 1938 "media btn long press - mute"); 1939 mCallAudioManager.toggleMute(); 1940 } 1941 return true; 1942 } 1943 } 1944 return false; 1945 } 1946 1947 /** 1948 * Returns true if telecom supports adding another top-level call. 1949 */ 1950 @VisibleForTesting 1951 public boolean canAddCall() { 1952 boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(), 1953 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 1954 if (!isDeviceProvisioned) { 1955 Log.d(TAG, "Device not provisioned, canAddCall is false."); 1956 return false; 1957 } 1958 1959 if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) { 1960 return false; 1961 } 1962 1963 int count = 0; 1964 for (Call call : mCalls) { 1965 if (call.isEmergencyCall()) { 1966 // We never support add call if one of the calls is an emergency call. 1967 return false; 1968 } else if (call.isExternalCall()) { 1969 // External calls don't count. 1970 continue; 1971 } else if (call.getParentCall() == null) { 1972 count++; 1973 } 1974 Bundle extras = call.getExtras(); 1975 if (extras != null) { 1976 if (extras.getBoolean(Connection.EXTRA_DISABLE_ADD_CALL, false)) { 1977 return false; 1978 } 1979 } 1980 1981 // We do not check states for canAddCall. We treat disconnected calls the same 1982 // and wait until they are removed instead. If we didn't count disconnected calls, 1983 // we could put InCallServices into a state where they are showing two calls but 1984 // also support add-call. Technically it's right, but overall looks better (UI-wise) 1985 // and acts better if we wait until the call is removed. 1986 if (count >= MAXIMUM_TOP_LEVEL_CALLS) { 1987 return false; 1988 } 1989 } 1990 1991 return true; 1992 } 1993 1994 @VisibleForTesting 1995 public Call getRingingCall() { 1996 return getFirstCallWithState(CallState.RINGING); 1997 } 1998 1999 public Call getActiveCall() { 2000 return getFirstCallWithState(CallState.ACTIVE); 2001 } 2002 2003 Call getDialingCall() { 2004 return getFirstCallWithState(CallState.DIALING); 2005 } 2006 2007 @VisibleForTesting 2008 public Call getHeldCall() { 2009 return getFirstCallWithState(CallState.ON_HOLD); 2010 } 2011 2012 @VisibleForTesting 2013 public int getNumHeldCalls() { 2014 int count = 0; 2015 for (Call call : mCalls) { 2016 if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) { 2017 count++; 2018 } 2019 } 2020 return count; 2021 } 2022 2023 @VisibleForTesting 2024 public Call getOutgoingCall() { 2025 return getFirstCallWithState(OUTGOING_CALL_STATES); 2026 } 2027 2028 @VisibleForTesting 2029 public Call getFirstCallWithState(int... states) { 2030 return getFirstCallWithState(null, states); 2031 } 2032 2033 @VisibleForTesting 2034 public PhoneNumberUtilsAdapter getPhoneNumberUtilsAdapter() { 2035 return mPhoneNumberUtilsAdapter; 2036 } 2037 2038 /** 2039 * Returns the first call that it finds with the given states. The states are treated as having 2040 * priority order so that any call with the first state will be returned before any call with 2041 * states listed later in the parameter list. 2042 * 2043 * @param callToSkip Call that this method should skip while searching 2044 */ 2045 Call getFirstCallWithState(Call callToSkip, int... states) { 2046 for (int currentState : states) { 2047 // check the foreground first 2048 Call foregroundCall = getForegroundCall(); 2049 if (foregroundCall != null && foregroundCall.getState() == currentState) { 2050 return foregroundCall; 2051 } 2052 2053 for (Call call : mCalls) { 2054 if (Objects.equals(callToSkip, call)) { 2055 continue; 2056 } 2057 2058 // Only operate on top-level calls 2059 if (call.getParentCall() != null) { 2060 continue; 2061 } 2062 2063 if (call.isExternalCall()) { 2064 continue; 2065 } 2066 2067 if (currentState == call.getState()) { 2068 return call; 2069 } 2070 } 2071 } 2072 return null; 2073 } 2074 2075 Call createConferenceCall( 2076 String callId, 2077 PhoneAccountHandle phoneAccount, 2078 ParcelableConference parcelableConference) { 2079 2080 // If the parceled conference specifies a connect time, use it; otherwise default to 0, 2081 // which is the default value for new Calls. 2082 long connectTime = 2083 parcelableConference.getConnectTimeMillis() == 2084 Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 : 2085 parcelableConference.getConnectTimeMillis(); 2086 long connectElapsedTime = 2087 parcelableConference.getConnectElapsedTimeMillis() == 2088 Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 : 2089 parcelableConference.getConnectElapsedTimeMillis(); 2090 2091 Call call = new Call( 2092 callId, 2093 mContext, 2094 this, 2095 mLock, 2096 mConnectionServiceRepository, 2097 mContactsAsyncHelper, 2098 mCallerInfoAsyncQueryFactory, 2099 mPhoneNumberUtilsAdapter, 2100 null /* handle */, 2101 null /* gatewayInfo */, 2102 null /* connectionManagerPhoneAccount */, 2103 phoneAccount, 2104 Call.CALL_DIRECTION_UNDEFINED /* callDirection */, 2105 false /* forceAttachToExistingConnection */, 2106 true /* isConference */, 2107 connectTime, 2108 connectElapsedTime, 2109 mClockProxy); 2110 2111 setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()), 2112 "new conference call"); 2113 call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities()); 2114 call.setConnectionProperties(parcelableConference.getConnectionProperties()); 2115 call.setVideoState(parcelableConference.getVideoState()); 2116 call.setVideoProvider(parcelableConference.getVideoProvider()); 2117 call.setStatusHints(parcelableConference.getStatusHints()); 2118 call.putExtras(Call.SOURCE_CONNECTION_SERVICE, parcelableConference.getExtras()); 2119 // In case this Conference was added via a ConnectionManager, keep track of the original 2120 // Connection ID as created by the originating ConnectionService. 2121 Bundle extras = parcelableConference.getExtras(); 2122 if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 2123 call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID)); 2124 } 2125 2126 // TODO: Move this to be a part of addCall() 2127 call.addListener(this); 2128 addCall(call); 2129 return call; 2130 } 2131 2132 /** 2133 * @return the call state currently tracked by {@link PhoneStateBroadcaster} 2134 */ 2135 int getCallState() { 2136 return mPhoneStateBroadcaster.getCallState(); 2137 } 2138 2139 /** 2140 * Retrieves the {@link PhoneAccountRegistrar}. 2141 * 2142 * @return The {@link PhoneAccountRegistrar}. 2143 */ 2144 PhoneAccountRegistrar getPhoneAccountRegistrar() { 2145 return mPhoneAccountRegistrar; 2146 } 2147 2148 /** 2149 * Retrieves the {@link MissedCallNotifier} 2150 * @return The {@link MissedCallNotifier}. 2151 */ 2152 MissedCallNotifier getMissedCallNotifier() { 2153 return mMissedCallNotifier; 2154 } 2155 2156 /** 2157 * Retrieves the {@link IncomingCallNotifier}. 2158 * @return The {@link IncomingCallNotifier}. 2159 */ 2160 IncomingCallNotifier getIncomingCallNotifier() { 2161 return mIncomingCallNotifier; 2162 } 2163 2164 /** 2165 * Reject an incoming call and manually add it to the Call Log. 2166 * @param incomingCall Incoming call that has been rejected 2167 */ 2168 private void rejectCallAndLog(Call incomingCall) { 2169 if (incomingCall.getConnectionService() != null) { 2170 // Only reject the call if it has not already been destroyed. If a call ends while 2171 // incoming call filtering is taking place, it is possible that the call has already 2172 // been destroyed, and as such it will be impossible to send the reject to the 2173 // associated ConnectionService. 2174 incomingCall.reject(false, null); 2175 } else { 2176 Log.i(this, "rejectCallAndLog - call already destroyed."); 2177 } 2178 2179 // Since the call was not added to the list of calls, we have to call the missed 2180 // call notifier and the call logger manually. 2181 // Do we need missed call notification for direct to Voicemail calls? 2182 mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE, 2183 true /*showNotificationForMissedCall*/); 2184 } 2185 2186 /** 2187 * Adds the specified call to the main list of live calls. 2188 * 2189 * @param call The call to add. 2190 */ 2191 @VisibleForTesting 2192 public void addCall(Call call) { 2193 Trace.beginSection("addCall"); 2194 Log.v(this, "addCall(%s)", call); 2195 call.addListener(this); 2196 mCalls.add(call); 2197 2198 // Specifies the time telecom finished routing the call. This is used by the dialer for 2199 // analytics. 2200 Bundle extras = call.getIntentExtras(); 2201 extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS, 2202 SystemClock.elapsedRealtime()); 2203 2204 updateCanAddCall(); 2205 // onCallAdded for calls which immediately take the foreground (like the first call). 2206 for (CallsManagerListener listener : mListeners) { 2207 if (LogUtils.SYSTRACE_DEBUG) { 2208 Trace.beginSection(listener.getClass().toString() + " addCall"); 2209 } 2210 listener.onCallAdded(call); 2211 if (LogUtils.SYSTRACE_DEBUG) { 2212 Trace.endSection(); 2213 } 2214 } 2215 Trace.endSection(); 2216 } 2217 2218 private void removeCall(Call call) { 2219 Trace.beginSection("removeCall"); 2220 Log.v(this, "removeCall(%s)", call); 2221 2222 call.setParentAndChildCall(null); // clean up parent relationship before destroying. 2223 call.removeListener(this); 2224 call.clearConnectionService(); 2225 // TODO: clean up RTT pipes 2226 2227 boolean shouldNotify = false; 2228 if (mCalls.contains(call)) { 2229 mCalls.remove(call); 2230 shouldNotify = true; 2231 } 2232 2233 call.destroy(); 2234 2235 // Only broadcast changes for calls that are being tracked. 2236 if (shouldNotify) { 2237 updateCanAddCall(); 2238 for (CallsManagerListener listener : mListeners) { 2239 if (LogUtils.SYSTRACE_DEBUG) { 2240 Trace.beginSection(listener.getClass().toString() + " onCallRemoved"); 2241 } 2242 listener.onCallRemoved(call); 2243 if (LogUtils.SYSTRACE_DEBUG) { 2244 Trace.endSection(); 2245 } 2246 } 2247 } 2248 Trace.endSection(); 2249 } 2250 2251 /** 2252 * Sets the specified state on the specified call. 2253 * 2254 * @param call The call. 2255 * @param newState The new state of the call. 2256 */ 2257 private void setCallState(Call call, int newState, String tag) { 2258 if (call == null) { 2259 return; 2260 } 2261 int oldState = call.getState(); 2262 Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState), 2263 CallState.toString(newState), call); 2264 if (newState != oldState) { 2265 // Unfortunately, in the telephony world the radio is king. So if the call notifies 2266 // us that the call is in a particular state, we allow it even if it doesn't make 2267 // sense (e.g., STATE_ACTIVE -> STATE_RINGING). 2268 // TODO: Consider putting a stop to the above and turning CallState 2269 // into a well-defined state machine. 2270 // TODO: Define expected state transitions here, and log when an 2271 // unexpected transition occurs. 2272 call.setState(newState, tag); 2273 maybeShowErrorDialogOnDisconnect(call); 2274 2275 Trace.beginSection("onCallStateChanged"); 2276 2277 maybeHandleHandover(call, newState); 2278 2279 // Only broadcast state change for calls that are being tracked. 2280 if (mCalls.contains(call)) { 2281 updateCanAddCall(); 2282 for (CallsManagerListener listener : mListeners) { 2283 if (LogUtils.SYSTRACE_DEBUG) { 2284 Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); 2285 } 2286 listener.onCallStateChanged(call, oldState, newState); 2287 if (LogUtils.SYSTRACE_DEBUG) { 2288 Trace.endSection(); 2289 } 2290 } 2291 } 2292 Trace.endSection(); 2293 } 2294 } 2295 2296 /** 2297 * Identifies call state transitions for a call which trigger handover events. 2298 * - If this call has a handover to it which just started and this call goes active, treat 2299 * this as if the user accepted the handover. 2300 * - If this call has a handover to it which just started and this call is disconnected, treat 2301 * this as if the user rejected the handover. 2302 * - If this call has a handover from it which just started and this call is disconnected, do 2303 * nothing as the call prematurely disconnected before the user accepted the handover. 2304 * - If this call has a handover from it which was already accepted by the user and this call is 2305 * disconnected, mark the handover as complete. 2306 * 2307 * @param call A call whose state is changing. 2308 * @param newState The new state of the call. 2309 */ 2310 private void maybeHandleHandover(Call call, int newState) { 2311 if (call.getHandoverSourceCall() != null) { 2312 // We are handing over another call to this one. 2313 if (call.getHandoverState() == HandoverState.HANDOVER_TO_STARTED) { 2314 // A handover to this call has just been initiated. 2315 if (newState == CallState.ACTIVE) { 2316 // This call went active, so the user has accepted the handover. 2317 Log.i(this, "setCallState: handover to accepted"); 2318 acceptHandoverTo(call); 2319 } else if (newState == CallState.DISCONNECTED) { 2320 // The call was disconnected, so the user has rejected the handover. 2321 Log.i(this, "setCallState: handover to rejected"); 2322 rejectHandoverTo(call); 2323 } 2324 } 2325 // If this call was disconnected because it was handed over TO another call, report the 2326 // handover as complete. 2327 } else if (call.getHandoverDestinationCall() != null 2328 && newState == CallState.DISCONNECTED) { 2329 int handoverState = call.getHandoverState(); 2330 if (handoverState == HandoverState.HANDOVER_FROM_STARTED) { 2331 // Disconnect before handover was accepted. 2332 Log.i(this, "setCallState: disconnect before handover accepted"); 2333 // Let the handover destination know that the source has disconnected prior to 2334 // completion of the handover. 2335 call.getHandoverDestinationCall().sendCallEvent( 2336 android.telecom.Call.EVENT_HANDOVER_SOURCE_DISCONNECTED, null); 2337 } else if (handoverState == HandoverState.HANDOVER_ACCEPTED) { 2338 Log.i(this, "setCallState: handover from complete"); 2339 completeHandoverFrom(call); 2340 } 2341 } 2342 } 2343 2344 private void completeHandoverFrom(Call call) { 2345 Call handoverTo = call.getHandoverDestinationCall(); 2346 Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", 2347 call.getId(), handoverTo.getId()); 2348 Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", 2349 call.getId(), handoverTo.getId()); 2350 2351 // Inform the "from" Call (ie the source call) that the handover from it has 2352 // completed; this allows the InCallService to be notified that a handover it 2353 // initiated completed. 2354 call.onConnectionEvent(Connection.EVENT_HANDOVER_COMPLETE, null); 2355 // Inform the "to" ConnectionService that handover to it has completed. 2356 handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null); 2357 answerCall(handoverTo, handoverTo.getVideoState()); 2358 call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE); 2359 2360 // If the call we handed over to is self-managed, we need to disconnect the calls for other 2361 // ConnectionServices. 2362 if (handoverTo.isSelfManaged()) { 2363 disconnectOtherCalls(handoverTo.getTargetPhoneAccount()); 2364 } 2365 } 2366 2367 private void rejectHandoverTo(Call handoverTo) { 2368 Call handoverFrom = handoverTo.getHandoverSourceCall(); 2369 Log.i(this, "rejectHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId()); 2370 Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s", 2371 handoverTo.getId(), handoverFrom.getId()); 2372 Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s", 2373 handoverTo.getId(), handoverFrom.getId()); 2374 2375 // Inform the "from" Call (ie the source call) that the handover from it has 2376 // failed; this allows the InCallService to be notified that a handover it 2377 // initiated failed. 2378 handoverFrom.onConnectionEvent(Connection.EVENT_HANDOVER_FAILED, null); 2379 // Inform the "to" ConnectionService that handover to it has failed. This 2380 // allows the ConnectionService the call was being handed over 2381 if (handoverTo.getConnectionService() != null) { 2382 // Only attempt if the call has a bound ConnectionService if handover failed 2383 // early on in the handover process, the CS will be unbound and we won't be 2384 // able to send the call event. 2385 handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null); 2386 } 2387 handoverTo.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_FAILED); 2388 } 2389 2390 private void acceptHandoverTo(Call handoverTo) { 2391 Call handoverFrom = handoverTo.getHandoverSourceCall(); 2392 Log.i(this, "acceptHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId()); 2393 handoverTo.setHandoverState(HandoverState.HANDOVER_ACCEPTED); 2394 handoverFrom.setHandoverState(HandoverState.HANDOVER_ACCEPTED); 2395 2396 Log.addEvent(handoverTo, LogUtils.Events.ACCEPT_HANDOVER, "from=%s, to=%s", 2397 handoverFrom.getId(), handoverTo.getId()); 2398 Log.addEvent(handoverFrom, LogUtils.Events.ACCEPT_HANDOVER, "from=%s, to=%s", 2399 handoverFrom.getId(), handoverTo.getId()); 2400 2401 // Disconnect the call we handed over from. 2402 disconnectCall(handoverFrom); 2403 // If we handed over to a self-managed ConnectionService, we need to disconnect calls for 2404 // other ConnectionServices. 2405 if (handoverTo.isSelfManaged()) { 2406 disconnectOtherCalls(handoverTo.getTargetPhoneAccount()); 2407 } 2408 } 2409 2410 private void updateCanAddCall() { 2411 boolean newCanAddCall = canAddCall(); 2412 if (newCanAddCall != mCanAddCall) { 2413 mCanAddCall = newCanAddCall; 2414 for (CallsManagerListener listener : mListeners) { 2415 if (LogUtils.SYSTRACE_DEBUG) { 2416 Trace.beginSection(listener.getClass().toString() + " updateCanAddCall"); 2417 } 2418 listener.onCanAddCallChanged(mCanAddCall); 2419 if (LogUtils.SYSTRACE_DEBUG) { 2420 Trace.endSection(); 2421 } 2422 } 2423 } 2424 } 2425 2426 private boolean isPotentialMMICode(Uri handle) { 2427 return (handle != null && handle.getSchemeSpecificPart() != null 2428 && handle.getSchemeSpecificPart().contains("#")); 2429 } 2430 2431 /** 2432 * Determines if a dialed number is potentially an In-Call MMI code. In-Call MMI codes are 2433 * MMI codes which can be dialed when one or more calls are in progress. 2434 * <P> 2435 * Checks for numbers formatted similar to the MMI codes defined in: 2436 * {@link com.android.internal.telephony.Phone#handleInCallMmiCommands(String)} 2437 * 2438 * @param handle The URI to call. 2439 * @return {@code True} if the URI represents a number which could be an in-call MMI code. 2440 */ 2441 private boolean isPotentialInCallMMICode(Uri handle) { 2442 if (handle != null && handle.getSchemeSpecificPart() != null && 2443 handle.getScheme() != null && 2444 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) { 2445 2446 String dialedNumber = handle.getSchemeSpecificPart(); 2447 return (dialedNumber.equals("0") || 2448 (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) || 2449 (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) || 2450 dialedNumber.equals("3") || 2451 dialedNumber.equals("4") || 2452 dialedNumber.equals("5")); 2453 } 2454 return false; 2455 } 2456 2457 @VisibleForTesting 2458 public int getNumCallsWithState(final boolean isSelfManaged, Call excludeCall, 2459 PhoneAccountHandle phoneAccountHandle, int... states) { 2460 return getNumCallsWithState(isSelfManaged ? CALL_FILTER_SELF_MANAGED : CALL_FILTER_MANAGED, 2461 excludeCall, phoneAccountHandle, states); 2462 } 2463 2464 /** 2465 * Determines the number of calls matching the specified criteria. 2466 * @param callFilter indicates whether to include just managed calls 2467 * ({@link #CALL_FILTER_MANAGED}), self-managed calls 2468 * ({@link #CALL_FILTER_SELF_MANAGED}), or all calls 2469 * ({@link #CALL_FILTER_ALL}). 2470 * @param excludeCall Where {@code non-null}, this call is excluded from the count. 2471 * @param phoneAccountHandle Where {@code non-null}, calls for this {@link PhoneAccountHandle} 2472 * are excluded from the count. 2473 * @param states The list of {@link CallState}s to include in the count. 2474 * @return Count of calls matching criteria. 2475 */ 2476 @VisibleForTesting 2477 public int getNumCallsWithState(final int callFilter, Call excludeCall, 2478 PhoneAccountHandle phoneAccountHandle, int... states) { 2479 2480 Set<Integer> desiredStates = IntStream.of(states).boxed().collect(Collectors.toSet()); 2481 2482 Stream<Call> callsStream = mCalls.stream() 2483 .filter(call -> desiredStates.contains(call.getState()) && 2484 call.getParentCall() == null && !call.isExternalCall()); 2485 2486 if (callFilter == CALL_FILTER_MANAGED) { 2487 callsStream = callsStream.filter(call -> !call.isSelfManaged()); 2488 } else if (callFilter == CALL_FILTER_SELF_MANAGED) { 2489 callsStream = callsStream.filter(call -> call.isSelfManaged()); 2490 } 2491 2492 // If a call to exclude was specified, filter it out. 2493 if (excludeCall != null) { 2494 callsStream = callsStream.filter(call -> call != excludeCall); 2495 } 2496 2497 // If a phone account handle was specified, only consider calls for that phone account. 2498 if (phoneAccountHandle != null) { 2499 callsStream = callsStream.filter( 2500 call -> phoneAccountHandle.equals(call.getTargetPhoneAccount())); 2501 } 2502 2503 return (int) callsStream.count(); 2504 } 2505 2506 private boolean hasMaximumManagedLiveCalls(Call exceptCall) { 2507 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(false /* isSelfManaged */, 2508 exceptCall, null /* phoneAccountHandle */, LIVE_CALL_STATES); 2509 } 2510 2511 private boolean hasMaximumSelfManagedCalls(Call exceptCall, 2512 PhoneAccountHandle phoneAccountHandle) { 2513 return MAXIMUM_SELF_MANAGED_CALLS <= getNumCallsWithState(true /* isSelfManaged */, 2514 exceptCall, phoneAccountHandle, ANY_CALL_STATE); 2515 } 2516 2517 private boolean hasMaximumManagedHoldingCalls(Call exceptCall) { 2518 return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, 2519 null /* phoneAccountHandle */, CallState.ON_HOLD); 2520 } 2521 2522 private boolean hasMaximumManagedRingingCalls(Call exceptCall) { 2523 return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, 2524 null /* phoneAccountHandle */, CallState.RINGING); 2525 } 2526 2527 private boolean hasMaximumSelfManagedRingingCalls(Call exceptCall, 2528 PhoneAccountHandle phoneAccountHandle) { 2529 return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(true /* isSelfManaged */, exceptCall, 2530 phoneAccountHandle, CallState.RINGING); 2531 } 2532 2533 private boolean hasMaximumManagedOutgoingCalls(Call exceptCall) { 2534 return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, 2535 null /* phoneAccountHandle */, OUTGOING_CALL_STATES); 2536 } 2537 2538 private boolean hasMaximumManagedDialingCalls(Call exceptCall) { 2539 return MAXIMUM_DIALING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, 2540 null /* phoneAccountHandle */, CallState.DIALING, CallState.PULLING); 2541 } 2542 2543 /** 2544 * Given a {@link PhoneAccountHandle} determines if there are calls owned by any other 2545 * {@link PhoneAccountHandle}. 2546 * @param phoneAccountHandle The {@link PhoneAccountHandle} to check. 2547 * @return {@code true} if there are other calls, {@code false} otherwise. 2548 */ 2549 public boolean hasCallsForOtherPhoneAccount(PhoneAccountHandle phoneAccountHandle) { 2550 return getNumCallsForOtherPhoneAccount(phoneAccountHandle) > 0; 2551 } 2552 2553 /** 2554 * Determines the number of calls present for PhoneAccounts other than the one specified. 2555 * @param phoneAccountHandle The handle of the PhoneAccount. 2556 * @return Number of calls owned by other PhoneAccounts. 2557 */ 2558 public int getNumCallsForOtherPhoneAccount(PhoneAccountHandle phoneAccountHandle) { 2559 return (int) mCalls.stream().filter(call -> 2560 !phoneAccountHandle.equals(call.getTargetPhoneAccount()) && 2561 call.getParentCall() == null && 2562 !call.isExternalCall()).count(); 2563 } 2564 2565 /** 2566 * Determines if there are any managed calls. 2567 * @return {@code true} if there are managed calls, {@code false} otherwise. 2568 */ 2569 public boolean hasManagedCalls() { 2570 return mCalls.stream().filter(call -> !call.isSelfManaged() && 2571 !call.isExternalCall()).count() > 0; 2572 } 2573 2574 /** 2575 * Determines if there are any self-managed calls. 2576 * @return {@code true} if there are self-managed calls, {@code false} otherwise. 2577 */ 2578 public boolean hasSelfManagedCalls() { 2579 return mCalls.stream().filter(call -> call.isSelfManaged()).count() > 0; 2580 } 2581 2582 /** 2583 * Determines if there are any ongoing managed or self-managed calls. 2584 * Note: The {@link #ONGOING_CALL_STATES} are 2585 * @return {@code true} if there are ongoing managed or self-managed calls, {@code false} 2586 * otherwise. 2587 */ 2588 public boolean hasOngoingCalls() { 2589 return getNumCallsWithState( 2590 CALL_FILTER_ALL, null /* excludeCall */, 2591 null /* phoneAccountHandle */, 2592 ONGOING_CALL_STATES) > 0; 2593 } 2594 2595 /** 2596 * Determines if there are any ongoing managed calls. 2597 * @return {@code true} if there are ongoing managed calls, {@code false} otherwise. 2598 */ 2599 public boolean hasOngoingManagedCalls() { 2600 return getNumCallsWithState( 2601 CALL_FILTER_MANAGED, null /* excludeCall */, 2602 null /* phoneAccountHandle */, 2603 ONGOING_CALL_STATES) > 0; 2604 } 2605 2606 /** 2607 * Determines if the system incoming call UI should be shown. 2608 * The system incoming call UI will be shown if the new incoming call is self-managed, and there 2609 * are ongoing calls for another PhoneAccount. 2610 * @param incomingCall The incoming call. 2611 * @return {@code true} if the system incoming call UI should be shown, {@code false} otherwise. 2612 */ 2613 public boolean shouldShowSystemIncomingCallUi(Call incomingCall) { 2614 return incomingCall.isIncoming() && incomingCall.isSelfManaged() && 2615 hasCallsForOtherPhoneAccount(incomingCall.getTargetPhoneAccount()) && 2616 incomingCall.getHandoverSourceCall() == null; 2617 } 2618 2619 private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) { 2620 if (hasMaximumManagedLiveCalls(call)) { 2621 // NOTE: If the amount of live calls changes beyond 1, this logic will probably 2622 // have to change. 2623 Call liveCall = getFirstCallWithState(LIVE_CALL_STATES); 2624 Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " + 2625 liveCall); 2626 2627 if (call == liveCall) { 2628 // If the call is already the foreground call, then we are golden. 2629 // This can happen after the user selects an account in the SELECT_PHONE_ACCOUNT 2630 // state since the call was already populated into the list. 2631 return true; 2632 } 2633 2634 if (hasMaximumManagedOutgoingCalls(call)) { 2635 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES); 2636 if (isEmergency && !outgoingCall.isEmergencyCall()) { 2637 // Disconnect the current outgoing call if it's not an emergency call. If the 2638 // user tries to make two outgoing calls to different emergency call numbers, 2639 // we will try to connect the first outgoing call. 2640 call.getAnalytics().setCallIsAdditional(true); 2641 outgoingCall.getAnalytics().setCallIsInterrupted(true); 2642 outgoingCall.disconnect(); 2643 return true; 2644 } 2645 if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) { 2646 // If there is an orphaned call in the {@link CallState#SELECT_PHONE_ACCOUNT} 2647 // state, just disconnect it since the user has explicitly started a new call. 2648 call.getAnalytics().setCallIsAdditional(true); 2649 outgoingCall.getAnalytics().setCallIsInterrupted(true); 2650 outgoingCall.disconnect(); 2651 return true; 2652 } 2653 return false; 2654 } 2655 2656 if (hasMaximumManagedHoldingCalls(call)) { 2657 // There is no more room for any more calls, unless it's an emergency. 2658 if (isEmergency) { 2659 // Kill the current active call, this is easier then trying to disconnect a 2660 // holding call and hold an active call. 2661 call.getAnalytics().setCallIsAdditional(true); 2662 liveCall.getAnalytics().setCallIsInterrupted(true); 2663 liveCall.disconnect(); 2664 return true; 2665 } 2666 return false; // No more room! 2667 } 2668 2669 // We have room for at least one more holding call at this point. 2670 2671 // TODO: Remove once b/23035408 has been corrected. 2672 // If the live call is a conference, it will not have a target phone account set. This 2673 // means the check to see if the live call has the same target phone account as the new 2674 // call will not cause us to bail early. As a result, we'll end up holding the 2675 // ongoing conference call. However, the ConnectionService is already doing that. This 2676 // has caused problems with some carriers. As a workaround until b/23035408 is 2677 // corrected, we will try and get the target phone account for one of the conference's 2678 // children and use that instead. 2679 PhoneAccountHandle liveCallPhoneAccount = liveCall.getTargetPhoneAccount(); 2680 if (liveCallPhoneAccount == null && liveCall.isConference() && 2681 !liveCall.getChildCalls().isEmpty()) { 2682 liveCallPhoneAccount = getFirstChildPhoneAccount(liveCall); 2683 Log.i(this, "makeRoomForOutgoingCall: using child call PhoneAccount = " + 2684 liveCallPhoneAccount); 2685 } 2686 2687 // First thing, if we are trying to make a call with the same phone account as the live 2688 // call, then allow it so that the connection service can make its own decision about 2689 // how to handle the new call relative to the current one. 2690 if (Objects.equals(liveCallPhoneAccount, call.getTargetPhoneAccount())) { 2691 Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches."); 2692 call.getAnalytics().setCallIsAdditional(true); 2693 liveCall.getAnalytics().setCallIsInterrupted(true); 2694 return true; 2695 } else if (call.getTargetPhoneAccount() == null) { 2696 // Without a phone account, we can't say reliably that the call will fail. 2697 // If the user chooses the same phone account as the live call, then it's 2698 // still possible that the call can be made (like with CDMA calls not supporting 2699 // hold but they still support adding a call by going immediately into conference 2700 // mode). Return true here and we'll run this code again after user chooses an 2701 // account. 2702 return true; 2703 } 2704 2705 // Try to hold the live call before attempting the new outgoing call. 2706 if (liveCall.can(Connection.CAPABILITY_HOLD)) { 2707 Log.i(this, "makeRoomForOutgoingCall: holding live call."); 2708 call.getAnalytics().setCallIsAdditional(true); 2709 liveCall.getAnalytics().setCallIsInterrupted(true); 2710 liveCall.hold(); 2711 return true; 2712 } 2713 2714 // The live call cannot be held so we're out of luck here. There's no room. 2715 return false; 2716 } 2717 return true; 2718 } 2719 2720 /** 2721 * Given a call, find the first non-null phone account handle of its children. 2722 * 2723 * @param parentCall The parent call. 2724 * @return The first non-null phone account handle of the children, or {@code null} if none. 2725 */ 2726 private PhoneAccountHandle getFirstChildPhoneAccount(Call parentCall) { 2727 for (Call childCall : parentCall.getChildCalls()) { 2728 PhoneAccountHandle childPhoneAccount = childCall.getTargetPhoneAccount(); 2729 if (childPhoneAccount != null) { 2730 return childPhoneAccount; 2731 } 2732 } 2733 return null; 2734 } 2735 2736 /** 2737 * Checks to see if the call should be on speakerphone and if so, set it. 2738 */ 2739 private void maybeMoveToSpeakerPhone(Call call) { 2740 if (call.isHandoverInProgress() && call.getState() == CallState.DIALING) { 2741 // When a new outgoing call is initiated for the purpose of handing over, do not engage 2742 // speaker automatically until the call goes active. 2743 return; 2744 } 2745 if (call.getStartWithSpeakerphoneOn()) { 2746 setAudioRoute(CallAudioState.ROUTE_SPEAKER, null); 2747 call.setStartWithSpeakerphoneOn(false); 2748 } 2749 } 2750 2751 /** 2752 * Creates a new call for an existing connection. 2753 * 2754 * @param callId The id of the new call. 2755 * @param connection The connection information. 2756 * @return The new call. 2757 */ 2758 Call createCallForExistingConnection(String callId, ParcelableConnection connection) { 2759 boolean isDowngradedConference = (connection.getConnectionProperties() 2760 & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0; 2761 Call call = new Call( 2762 callId, 2763 mContext, 2764 this, 2765 mLock, 2766 mConnectionServiceRepository, 2767 mContactsAsyncHelper, 2768 mCallerInfoAsyncQueryFactory, 2769 mPhoneNumberUtilsAdapter, 2770 connection.getHandle() /* handle */, 2771 null /* gatewayInfo */, 2772 null /* connectionManagerPhoneAccount */, 2773 connection.getPhoneAccount(), /* targetPhoneAccountHandle */ 2774 Call.CALL_DIRECTION_UNDEFINED /* callDirection */, 2775 false /* forceAttachToExistingConnection */, 2776 isDowngradedConference /* isConference */, 2777 connection.getConnectTimeMillis() /* connectTimeMillis */, 2778 connection.getConnectElapsedTimeMillis(), /* connectElapsedTimeMillis */ 2779 mClockProxy); 2780 2781 call.initAnalytics(); 2782 call.getAnalytics().setCreatedFromExistingConnection(true); 2783 2784 setCallState(call, Call.getStateFromConnectionState(connection.getState()), 2785 "existing connection"); 2786 call.setConnectionCapabilities(connection.getConnectionCapabilities()); 2787 call.setConnectionProperties(connection.getConnectionProperties()); 2788 call.setHandle(connection.getHandle(), connection.getHandlePresentation()); 2789 call.setCallerDisplayName(connection.getCallerDisplayName(), 2790 connection.getCallerDisplayNamePresentation()); 2791 call.addListener(this); 2792 2793 // In case this connection was added via a ConnectionManager, keep track of the original 2794 // Connection ID as created by the originating ConnectionService. 2795 Bundle extras = connection.getExtras(); 2796 if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 2797 call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID)); 2798 } 2799 Log.i(this, "createCallForExistingConnection: %s", connection); 2800 Call parentCall = null; 2801 if (!TextUtils.isEmpty(connection.getParentCallId())) { 2802 String parentId = connection.getParentCallId(); 2803 parentCall = mCalls 2804 .stream() 2805 .filter(c -> c.getId().equals(parentId)) 2806 .findFirst() 2807 .orElse(null); 2808 if (parentCall != null) { 2809 Log.i(this, "createCallForExistingConnection: %s added as child of %s.", 2810 call.getId(), 2811 parentCall.getId()); 2812 // Set JUST the parent property, which won't send an update to the Incall UI. 2813 call.setParentCall(parentCall); 2814 } 2815 } 2816 addCall(call); 2817 if (parentCall != null) { 2818 // Now, set the call as a child of the parent since it has been added to Telecom. This 2819 // is where we will inform InCall. 2820 call.setChildOf(parentCall); 2821 call.notifyParentChanged(parentCall); 2822 } 2823 2824 return call; 2825 } 2826 2827 /** 2828 * Determines whether Telecom already knows about a Connection added via the 2829 * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle, 2830 * Connection)} API via a ConnectionManager. 2831 * 2832 * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}. 2833 * @param originalConnectionId The new connection ID to check. 2834 * @return {@code true} if this connection is already known by Telecom. 2835 */ 2836 Call getAlreadyAddedConnection(String originalConnectionId) { 2837 Optional<Call> existingCall = mCalls.stream() 2838 .filter(call -> originalConnectionId.equals(call.getOriginalConnectionId()) || 2839 originalConnectionId.equals(call.getId())) 2840 .findFirst(); 2841 2842 if (existingCall.isPresent()) { 2843 Log.i(this, "isExistingConnectionAlreadyAdded - call %s already added with id %s", 2844 originalConnectionId, existingCall.get().getId()); 2845 return existingCall.get(); 2846 } 2847 2848 return null; 2849 } 2850 2851 /** 2852 * @return A new unique telecom call Id. 2853 */ 2854 private String getNextCallId() { 2855 synchronized(mLock) { 2856 return TELECOM_CALL_ID_PREFIX + (++mCallId); 2857 } 2858 } 2859 2860 public int getNextRttRequestId() { 2861 synchronized (mLock) { 2862 return (++mRttRequestId); 2863 } 2864 } 2865 2866 /** 2867 * Callback when foreground user is switched. We will reload missed call in all profiles 2868 * including the user itself. There may be chances that profiles are not started yet. 2869 */ 2870 @VisibleForTesting 2871 public void onUserSwitch(UserHandle userHandle) { 2872 mCurrentUserHandle = userHandle; 2873 mMissedCallNotifier.setCurrentUserHandle(userHandle); 2874 final UserManager userManager = UserManager.get(mContext); 2875 List<UserInfo> profiles = userManager.getEnabledProfiles(userHandle.getIdentifier()); 2876 for (UserInfo profile : profiles) { 2877 reloadMissedCallsOfUser(profile.getUserHandle()); 2878 } 2879 } 2880 2881 /** 2882 * Because there may be chances that profiles are not started yet though its parent user is 2883 * switched, we reload missed calls of profile that are just started here. 2884 */ 2885 void onUserStarting(UserHandle userHandle) { 2886 if (UserUtil.isProfile(mContext, userHandle)) { 2887 reloadMissedCallsOfUser(userHandle); 2888 } 2889 } 2890 2891 public TelecomSystem.SyncRoot getLock() { 2892 return mLock; 2893 } 2894 2895 private void reloadMissedCallsOfUser(UserHandle userHandle) { 2896 mMissedCallNotifier.reloadFromDatabase(mCallerInfoLookupHelper, 2897 new MissedCallNotifier.CallInfoFactory(), userHandle); 2898 } 2899 2900 public void onBootCompleted() { 2901 mMissedCallNotifier.reloadAfterBootComplete(mCallerInfoLookupHelper, 2902 new MissedCallNotifier.CallInfoFactory()); 2903 } 2904 2905 public boolean isIncomingCallPermitted(PhoneAccountHandle phoneAccountHandle) { 2906 return isIncomingCallPermitted(null /* excludeCall */, phoneAccountHandle); 2907 } 2908 2909 public boolean isIncomingCallPermitted(Call excludeCall, 2910 PhoneAccountHandle phoneAccountHandle) { 2911 if (phoneAccountHandle == null) { 2912 return false; 2913 } 2914 PhoneAccount phoneAccount = 2915 mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle); 2916 if (phoneAccount == null) { 2917 return false; 2918 } 2919 2920 if (!phoneAccount.isSelfManaged()) { 2921 return !hasMaximumManagedRingingCalls(excludeCall) && 2922 !hasMaximumManagedHoldingCalls(excludeCall); 2923 } else { 2924 return !hasEmergencyCall() && 2925 !hasMaximumSelfManagedRingingCalls(excludeCall, phoneAccountHandle) && 2926 !hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle); 2927 } 2928 } 2929 2930 public boolean isOutgoingCallPermitted(PhoneAccountHandle phoneAccountHandle) { 2931 return isOutgoingCallPermitted(null /* excludeCall */, phoneAccountHandle); 2932 } 2933 2934 public boolean isOutgoingCallPermitted(Call excludeCall, 2935 PhoneAccountHandle phoneAccountHandle) { 2936 if (phoneAccountHandle == null) { 2937 return false; 2938 } 2939 PhoneAccount phoneAccount = 2940 mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle); 2941 if (phoneAccount == null) { 2942 return false; 2943 } 2944 2945 if (!phoneAccount.isSelfManaged()) { 2946 return !hasMaximumManagedOutgoingCalls(excludeCall) && 2947 !hasMaximumManagedDialingCalls(excludeCall) && 2948 !hasMaximumManagedLiveCalls(excludeCall) && 2949 !hasMaximumManagedHoldingCalls(excludeCall); 2950 } else { 2951 // Only permit outgoing calls if there is no ongoing emergency calls and all other calls 2952 // are associated with the current PhoneAccountHandle. 2953 return !hasEmergencyCall() && ( 2954 (excludeCall != null && excludeCall.getHandoverSourceCall() != null) || ( 2955 !hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) 2956 && !hasCallsForOtherPhoneAccount(phoneAccountHandle) 2957 && !hasManagedCalls())); 2958 } 2959 } 2960 2961 /** 2962 * Blocks execution until all Telecom handlers have completed their current work. 2963 */ 2964 public void waitOnHandlers() { 2965 CountDownLatch mainHandlerLatch = new CountDownLatch(3); 2966 mHandler.post(() -> { 2967 mainHandlerLatch.countDown(); 2968 }); 2969 mCallAudioManager.getCallAudioModeStateMachine().getHandler().post(() -> { 2970 mainHandlerLatch.countDown(); 2971 }); 2972 mCallAudioManager.getCallAudioRouteStateMachine().getHandler().post(() -> { 2973 mainHandlerLatch.countDown(); 2974 }); 2975 2976 try { 2977 mainHandlerLatch.await(HANDLER_WAIT_TIMEOUT, TimeUnit.MILLISECONDS); 2978 } catch (InterruptedException e) { 2979 Log.w(this, "waitOnHandlers: interrupted %s", e); 2980 } 2981 } 2982 2983 /** 2984 * Used to confirm creation of an outgoing call which was marked as pending confirmation in 2985 * {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent)}. 2986 * Called via {@link TelecomBroadcastIntentProcessor} for a call which was confirmed via 2987 * {@link ConfirmCallDialogActivity}. 2988 * @param callId The call ID of the call to confirm. 2989 */ 2990 public void confirmPendingCall(String callId) { 2991 Log.i(this, "confirmPendingCall: callId=%s", callId); 2992 if (mPendingCall != null && mPendingCall.getId().equals(callId)) { 2993 Log.addEvent(mPendingCall, LogUtils.Events.USER_CONFIRMED); 2994 addCall(mPendingCall); 2995 2996 // We are going to place the new outgoing call, so disconnect any ongoing self-managed 2997 // calls which are ongoing at this time. 2998 disconnectSelfManagedCalls(); 2999 3000 // Kick of the new outgoing call intent from where it left off prior to confirming the 3001 // call. 3002 CallIntentProcessor.sendNewOutgoingCallIntent(mContext, mPendingCall, this, 3003 mPendingCall.getOriginalCallIntent()); 3004 mPendingCall = null; 3005 } 3006 } 3007 3008 /** 3009 * Used to cancel an outgoing call which was marked as pending confirmation in 3010 * {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent)}. 3011 * Called via {@link TelecomBroadcastIntentProcessor} for a call which was confirmed via 3012 * {@link ConfirmCallDialogActivity}. 3013 * @param callId The call ID of the call to cancel. 3014 */ 3015 public void cancelPendingCall(String callId) { 3016 Log.i(this, "cancelPendingCall: callId=%s", callId); 3017 if (mPendingCall != null && mPendingCall.getId().equals(callId)) { 3018 Log.addEvent(mPendingCall, LogUtils.Events.USER_CANCELLED); 3019 markCallAsDisconnected(mPendingCall, new DisconnectCause(DisconnectCause.CANCELED)); 3020 markCallAsRemoved(mPendingCall); 3021 mPendingCall = null; 3022 } 3023 } 3024 3025 /** 3026 * Called from {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent)} when 3027 * a managed call is added while there are ongoing self-managed calls. Starts 3028 * {@link ConfirmCallDialogActivity} to prompt the user to see if they wish to place the 3029 * outgoing call or not. 3030 * @param call The call to confirm. 3031 */ 3032 private void startCallConfirmation(Call call) { 3033 if (mPendingCall != null) { 3034 Log.i(this, "startCallConfirmation: call %s is already pending; disconnecting %s", 3035 mPendingCall.getId(), call.getId()); 3036 markCallDisconnectedDueToSelfManagedCall(call); 3037 return; 3038 } 3039 Log.addEvent(call, LogUtils.Events.USER_CONFIRMATION); 3040 mPendingCall = call; 3041 3042 // Figure out the name of the app in charge of the self-managed call(s). 3043 Call selfManagedCall = mCalls.stream() 3044 .filter(c -> c.isSelfManaged()) 3045 .findFirst() 3046 .orElse(null); 3047 CharSequence ongoingAppName = ""; 3048 if (selfManagedCall != null) { 3049 ongoingAppName = selfManagedCall.getTargetPhoneAccountLabel(); 3050 } 3051 Log.i(this, "startCallConfirmation: callId=%s, ongoingApp=%s", call.getId(), 3052 ongoingAppName); 3053 3054 Intent confirmIntent = new Intent(mContext, ConfirmCallDialogActivity.class); 3055 confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_OUTGOING_CALL_ID, call.getId()); 3056 confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_ONGOING_APP_NAME, ongoingAppName); 3057 confirmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3058 mContext.startActivityAsUser(confirmIntent, UserHandle.CURRENT); 3059 } 3060 3061 /** 3062 * Disconnects all self-managed calls. 3063 */ 3064 private void disconnectSelfManagedCalls() { 3065 // Disconnect all self-managed calls to make priority for emergency call. 3066 // Use Call.disconnect() to command the ConnectionService to disconnect the calls. 3067 // CallsManager.markCallAsDisconnected doesn't actually tell the ConnectionService to 3068 // disconnect. 3069 mCalls.stream() 3070 .filter(c -> c.isSelfManaged()) 3071 .forEach(c -> c.disconnect()); 3072 } 3073 3074 /** 3075 * Dumps the state of the {@link CallsManager}. 3076 * 3077 * @param pw The {@code IndentingPrintWriter} to write the state to. 3078 */ 3079 public void dump(IndentingPrintWriter pw) { 3080 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 3081 if (mCalls != null) { 3082 pw.println("mCalls: "); 3083 pw.increaseIndent(); 3084 for (Call call : mCalls) { 3085 pw.println(call); 3086 } 3087 pw.decreaseIndent(); 3088 } 3089 3090 if (mPendingCall != null) { 3091 pw.print("mPendingCall:"); 3092 pw.println(mPendingCall.getId()); 3093 } 3094 3095 if (mCallAudioManager != null) { 3096 pw.println("mCallAudioManager:"); 3097 pw.increaseIndent(); 3098 mCallAudioManager.dump(pw); 3099 pw.decreaseIndent(); 3100 } 3101 3102 if (mTtyManager != null) { 3103 pw.println("mTtyManager:"); 3104 pw.increaseIndent(); 3105 mTtyManager.dump(pw); 3106 pw.decreaseIndent(); 3107 } 3108 3109 if (mInCallController != null) { 3110 pw.println("mInCallController:"); 3111 pw.increaseIndent(); 3112 mInCallController.dump(pw); 3113 pw.decreaseIndent(); 3114 } 3115 3116 if (mDefaultDialerCache != null) { 3117 pw.println("mDefaultDialerCache:"); 3118 pw.increaseIndent(); 3119 mDefaultDialerCache.dumpCache(pw); 3120 pw.decreaseIndent(); 3121 } 3122 3123 if (mConnectionServiceRepository != null) { 3124 pw.println("mConnectionServiceRepository:"); 3125 pw.increaseIndent(); 3126 mConnectionServiceRepository.dump(pw); 3127 pw.decreaseIndent(); 3128 } 3129 } 3130 3131 /** 3132 * For some disconnected causes, we show a dialog when it's a mmi code or potential mmi code. 3133 * 3134 * @param call The call. 3135 */ 3136 private void maybeShowErrorDialogOnDisconnect(Call call) { 3137 if (call.getState() == CallState.DISCONNECTED && (isPotentialMMICode(call.getHandle()) 3138 || isPotentialInCallMMICode(call.getHandle())) && !mCalls.contains(call)) { 3139 DisconnectCause disconnectCause = call.getDisconnectCause(); 3140 if (!TextUtils.isEmpty(disconnectCause.getDescription()) && (disconnectCause.getCode() 3141 == DisconnectCause.ERROR)) { 3142 Intent errorIntent = new Intent(mContext, ErrorDialogActivity.class); 3143 errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_STRING_EXTRA, 3144 disconnectCause.getDescription()); 3145 errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 3146 mContext.startActivityAsUser(errorIntent, UserHandle.CURRENT); 3147 } 3148 } 3149 } 3150 3151 private void setIntentExtrasAndStartTime(Call call, Bundle extras) { 3152 // Create our own instance to modify (since extras may be Bundle.EMPTY) 3153 extras = new Bundle(extras); 3154 3155 // Specifies the time telecom began routing the call. This is used by the dialer for 3156 // analytics. 3157 extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS, 3158 SystemClock.elapsedRealtime()); 3159 3160 call.setIntentExtras(extras); 3161 } 3162 3163 /** 3164 * Notifies the {@link android.telecom.ConnectionService} associated with a 3165 * {@link PhoneAccountHandle} that the attempt to create a new connection has failed. 3166 * 3167 * @param phoneAccountHandle The {@link PhoneAccountHandle}. 3168 * @param call The {@link Call} which could not be added. 3169 */ 3170 private void notifyCreateConnectionFailed(PhoneAccountHandle phoneAccountHandle, Call call) { 3171 if (phoneAccountHandle == null) { 3172 return; 3173 } 3174 ConnectionServiceWrapper service = mConnectionServiceRepository.getService( 3175 phoneAccountHandle.getComponentName(), phoneAccountHandle.getUserHandle()); 3176 if (service == null) { 3177 Log.i(this, "Found no connection service."); 3178 return; 3179 } else { 3180 call.setConnectionService(service); 3181 service.createConnectionFailed(call); 3182 } 3183 } 3184 3185 /** 3186 * Called in response to a {@link Call} receiving a {@link Call#sendCallEvent(String, Bundle)} 3187 * of type {@link android.telecom.Call#EVENT_REQUEST_HANDOVER} indicating the 3188 * {@link android.telecom.InCallService} has requested a handover to another 3189 * {@link android.telecom.ConnectionService}. 3190 * 3191 * We will explicitly disallow a handover when there is an emergency call present. 3192 * 3193 * @param handoverFromCall The {@link Call} to be handed over. 3194 * @param handoverToHandle The {@link PhoneAccountHandle} to hand over the call to. 3195 * @param videoState The desired video state of {@link Call} after handover. 3196 * @param initiatingExtras Extras associated with the handover, to be passed to the handover 3197 * {@link android.telecom.ConnectionService}. 3198 */ 3199 private void requestHandover(Call handoverFromCall, PhoneAccountHandle handoverToHandle, 3200 int videoState, Bundle initiatingExtras) { 3201 3202 boolean isHandoverFromSupported = isHandoverFromPhoneAccountSupported( 3203 handoverFromCall.getTargetPhoneAccount()); 3204 boolean isHandoverToSupported = isHandoverToPhoneAccountSupported(handoverToHandle); 3205 3206 if (!isHandoverFromSupported || !isHandoverToSupported || hasEmergencyCall()) { 3207 handoverFromCall.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null); 3208 return; 3209 } 3210 3211 Log.addEvent(handoverFromCall, LogUtils.Events.HANDOVER_REQUEST, handoverToHandle); 3212 3213 Bundle extras = new Bundle(); 3214 extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER, true); 3215 extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, 3216 handoverFromCall.getTargetPhoneAccount()); 3217 extras.putInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, videoState); 3218 if (initiatingExtras != null) { 3219 extras.putAll(initiatingExtras); 3220 } 3221 extras.putParcelable(TelecomManager.EXTRA_CALL_AUDIO_STATE, 3222 mCallAudioManager.getCallAudioState()); 3223 Call handoverToCall = startOutgoingCall(handoverFromCall.getHandle(), handoverToHandle, 3224 extras, getCurrentUserHandle(), null /* originalIntent */); 3225 Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER, 3226 "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), handoverToCall.getId()); 3227 handoverFromCall.setHandoverDestinationCall(handoverToCall); 3228 handoverFromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); 3229 handoverToCall.setHandoverState(HandoverState.HANDOVER_TO_STARTED); 3230 handoverToCall.setHandoverSourceCall(handoverFromCall); 3231 handoverToCall.setNewOutgoingCallIntentBroadcastIsDone(); 3232 placeOutgoingCall(handoverToCall, handoverToCall.getHandle(), null /* gatewayInfo */, 3233 false /* startwithSpeaker */, 3234 videoState); 3235 } 3236 3237 /** 3238 * Determines if handover from the specified {@link PhoneAccountHandle} is supported. 3239 * 3240 * @param from The {@link PhoneAccountHandle} the handover originates from. 3241 * @return {@code true} if handover is currently allowed, {@code false} otherwise. 3242 */ 3243 private boolean isHandoverFromPhoneAccountSupported(PhoneAccountHandle from) { 3244 return getBooleanPhoneAccountExtra(from, PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM); 3245 } 3246 3247 /** 3248 * Determines if handover to the specified {@link PhoneAccountHandle} is supported. 3249 * 3250 * @param to The {@link PhoneAccountHandle} the handover it to. 3251 * @return {@code true} if handover is currently allowed, {@code false} otherwise. 3252 */ 3253 private boolean isHandoverToPhoneAccountSupported(PhoneAccountHandle to) { 3254 return getBooleanPhoneAccountExtra(to, PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO); 3255 } 3256 3257 /** 3258 * Retrieves a boolean phone account extra. 3259 * @param handle the {@link PhoneAccountHandle} to retrieve the extra for. 3260 * @param key The extras key. 3261 * @return {@code true} if the extra {@link PhoneAccount} extra is true, {@code false} 3262 * otherwise. 3263 */ 3264 private boolean getBooleanPhoneAccountExtra(PhoneAccountHandle handle, String key) { 3265 PhoneAccount phoneAccount = getPhoneAccountRegistrar().getPhoneAccountUnchecked(handle); 3266 if (phoneAccount == null) { 3267 return false; 3268 } 3269 3270 Bundle fromExtras = phoneAccount.getExtras(); 3271 if (fromExtras == null) { 3272 return false; 3273 } 3274 return fromExtras.getBoolean(key); 3275 } 3276 3277 /** 3278 * Determines if there is an existing handover in process. 3279 * @return {@code true} if a call in the process of handover exists, {@code false} otherwise. 3280 */ 3281 private boolean isHandoverInProgress() { 3282 return mCalls.stream().filter(c -> c.getHandoverSourceCall() != null || 3283 c.getHandoverDestinationCall() != null).count() > 0; 3284 } 3285 3286 private void broadcastUnregisterIntent(PhoneAccountHandle accountHandle) { 3287 Intent intent = 3288 new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED); 3289 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 3290 intent.putExtra( 3291 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle); 3292 Log.i(this, "Sending phone-account %s unregistered intent as user", accountHandle); 3293 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 3294 PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION); 3295 3296 String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication( 3297 getCurrentUserHandle().getIdentifier()); 3298 if (!TextUtils.isEmpty(dialerPackage)) { 3299 Intent directedIntent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED) 3300 .setPackage(dialerPackage); 3301 directedIntent.putExtra( 3302 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle); 3303 Log.i(this, "Sending phone-account unregistered intent to default dialer"); 3304 mContext.sendBroadcastAsUser(directedIntent, UserHandle.ALL, null); 3305 } 3306 return ; 3307 } 3308 3309 private void broadcastRegisterIntent(PhoneAccountHandle accountHandle) { 3310 Intent intent = new Intent( 3311 TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED); 3312 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 3313 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, 3314 accountHandle); 3315 Log.i(this, "Sending phone-account %s registered intent as user", accountHandle); 3316 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 3317 PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION); 3318 3319 String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication( 3320 getCurrentUserHandle().getIdentifier()); 3321 if (!TextUtils.isEmpty(dialerPackage)) { 3322 Intent directedIntent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED) 3323 .setPackage(dialerPackage); 3324 directedIntent.putExtra( 3325 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle); 3326 Log.i(this, "Sending phone-account registered intent to default dialer"); 3327 mContext.sendBroadcastAsUser(directedIntent, UserHandle.ALL, null); 3328 } 3329 return ; 3330 } 3331 3332 public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct) { 3333 // TODO: 3334 } 3335} 3336