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