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