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