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