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