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