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