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