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