CallsManager.java revision 4bc0245eb5c09ab48dcbb673eefdf7175ae8ac9c
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.Intent; 21import android.content.pm.PackageManager; 22import android.content.pm.ResolveInfo; 23import android.net.Uri; 24import android.os.Bundle; 25import android.os.Handler; 26import android.os.Trace; 27import android.provider.CallLog.Calls; 28import android.telecom.AudioState; 29import android.telecom.CallState; 30import android.telecom.DisconnectCause; 31import android.telecom.GatewayInfo; 32import android.telecom.ParcelableConference; 33import android.telecom.ParcelableConnection; 34import android.telecom.PhoneAccount; 35import android.telecom.PhoneAccountHandle; 36import android.telecom.PhoneCapabilities; 37import android.telecom.TelecomManager; 38import android.telecom.VideoProfile; 39import android.telephony.TelephonyManager; 40 41import com.android.internal.util.IndentingPrintWriter; 42 43import java.util.Collection; 44import java.util.Collections; 45import java.util.HashSet; 46import java.util.List; 47import java.util.Objects; 48import java.util.Set; 49import java.util.concurrent.ConcurrentHashMap; 50 51/** 52 * Singleton. 53 * 54 * NOTE: by design most APIs are package private, use the relevant adapter/s to allow 55 * access from other packages specifically refraining from passing the CallsManager instance 56 * beyond the com.android.server.telecom package boundary. 57 */ 58public final class CallsManager extends Call.ListenerBase { 59 60 // TODO: Consider renaming this CallsManagerPlugin. 61 interface CallsManagerListener { 62 void onCallAdded(Call call); 63 void onCallRemoved(Call call); 64 void onCallStateChanged(Call call, int oldState, int newState); 65 void onConnectionServiceChanged( 66 Call call, 67 ConnectionServiceWrapper oldService, 68 ConnectionServiceWrapper newService); 69 void onIncomingCallAnswered(Call call); 70 void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage); 71 void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall); 72 void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState); 73 void onRingbackRequested(Call call, boolean ringback); 74 void onIsConferencedChanged(Call call); 75 void onIsVoipAudioModeChanged(Call call); 76 void onVideoStateChanged(Call call); 77 void onCanAddCallChanged(boolean canAddCall); 78 } 79 80 /** 81 * Singleton instance of the {@link CallsManager}, initialized from {@link TelecomService}. 82 */ 83 private static CallsManager INSTANCE = null; 84 85 private static final String TAG = "CallsManager"; 86 87 private static final int MAXIMUM_LIVE_CALLS = 1; 88 private static final int MAXIMUM_HOLD_CALLS = 1; 89 private static final int MAXIMUM_RINGING_CALLS = 1; 90 private static final int MAXIMUM_OUTGOING_CALLS = 1; 91 private static final int MAXIMUM_TOP_LEVEL_CALLS = 2; 92 93 private static final int[] OUTGOING_CALL_STATES = 94 {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING}; 95 96 private static final int[] LIVE_CALL_STATES = 97 {CallState.CONNECTING, CallState.PRE_DIAL_WAIT, CallState.DIALING, CallState.ACTIVE}; 98 99 /** 100 * The main call repository. Keeps an instance of all live calls. New incoming and outgoing 101 * calls are added to the map and removed when the calls move to the disconnected state. 102 * 103 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is 104 * load factor before resizing, 1 means we only expect a single thread to 105 * access the map so make only a single shard 106 */ 107 private final Set<Call> mCalls = Collections.newSetFromMap( 108 new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1)); 109 110 private final ConnectionServiceRepository mConnectionServiceRepository; 111 private final DtmfLocalTonePlayer mDtmfLocalTonePlayer; 112 private final InCallController mInCallController; 113 private final CallAudioManager mCallAudioManager; 114 private final Ringer mRinger; 115 // For this set initial table size to 16 because we add 13 listeners in 116 // the CallsManager constructor. 117 private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap( 118 new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1)); 119 private final HeadsetMediaButton mHeadsetMediaButton; 120 private final WiredHeadsetManager mWiredHeadsetManager; 121 private final TtyManager mTtyManager; 122 private final ProximitySensorManager mProximitySensorManager; 123 private final PhoneStateBroadcaster mPhoneStateBroadcaster; 124 private final CallLogManager mCallLogManager; 125 private final Context mContext; 126 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 127 private final MissedCallNotifier mMissedCallNotifier; 128 private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>(); 129 private final Set<Call> mPendingCallsToDisconnect = new HashSet<>(); 130 /* Handler tied to thread in which CallManager was initialized. */ 131 private final Handler mHandler = new Handler(); 132 133 private boolean mCanAddCall = true; 134 135 /** 136 * The call the user is currently interacting with. This is the call that should have audio 137 * focus and be visible in the in-call UI. 138 */ 139 private Call mForegroundCall; 140 141 /** Singleton accessor. */ 142 static CallsManager getInstance() { 143 return INSTANCE; 144 } 145 146 /** 147 * Sets the static singleton instance. 148 * 149 * @param instance The instance to set. 150 */ 151 static void initialize(CallsManager instance) { 152 INSTANCE = instance; 153 } 154 155 /** 156 * Initializes the required Telecom components. 157 */ 158 CallsManager(Context context, MissedCallNotifier missedCallNotifier, 159 PhoneAccountRegistrar phoneAccountRegistrar) { 160 mContext = context; 161 mPhoneAccountRegistrar = phoneAccountRegistrar; 162 mMissedCallNotifier = missedCallNotifier; 163 StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this); 164 mWiredHeadsetManager = new WiredHeadsetManager(context); 165 mCallAudioManager = new CallAudioManager(context, statusBarNotifier, mWiredHeadsetManager); 166 InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory(mCallAudioManager); 167 mRinger = new Ringer(mCallAudioManager, this, playerFactory, context); 168 mHeadsetMediaButton = new HeadsetMediaButton(context, this); 169 mTtyManager = new TtyManager(context, mWiredHeadsetManager); 170 mProximitySensorManager = new ProximitySensorManager(context); 171 mPhoneStateBroadcaster = new PhoneStateBroadcaster(); 172 mCallLogManager = new CallLogManager(context); 173 mInCallController = new InCallController(context); 174 mDtmfLocalTonePlayer = new DtmfLocalTonePlayer(context); 175 mConnectionServiceRepository = new ConnectionServiceRepository(mPhoneAccountRegistrar, 176 context); 177 178 mListeners.add(statusBarNotifier); 179 mListeners.add(mCallLogManager); 180 mListeners.add(mPhoneStateBroadcaster); 181 mListeners.add(mInCallController); 182 mListeners.add(mRinger); 183 mListeners.add(new RingbackPlayer(this, playerFactory)); 184 mListeners.add(new InCallToneMonitor(playerFactory, this)); 185 mListeners.add(mCallAudioManager); 186 mListeners.add(missedCallNotifier); 187 mListeners.add(mDtmfLocalTonePlayer); 188 mListeners.add(mHeadsetMediaButton); 189 mListeners.add(RespondViaSmsManager.getInstance()); 190 mListeners.add(mProximitySensorManager); 191 } 192 193 @Override 194 public void onSuccessfulOutgoingCall(Call call, int callState) { 195 Log.v(this, "onSuccessfulOutgoingCall, %s", call); 196 197 setCallState(call, callState); 198 if (!mCalls.contains(call)) { 199 // Call was not added previously in startOutgoingCall due to it being a potential MMI 200 // code, so add it now. 201 addCall(call); 202 } 203 204 // The call's ConnectionService has been updated. 205 for (CallsManagerListener listener : mListeners) { 206 listener.onConnectionServiceChanged(call, null, call.getConnectionService()); 207 } 208 209 markCallAsDialing(call); 210 } 211 212 @Override 213 public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) { 214 Log.v(this, "onFailedOutgoingCall, call: %s", call); 215 216 markCallAsRemoved(call); 217 } 218 219 @Override 220 public void onSuccessfulIncomingCall(Call incomingCall) { 221 Log.d(this, "onSuccessfulIncomingCall"); 222 setCallState(incomingCall, CallState.RINGING); 223 224 if (hasMaximumRingingCalls()) { 225 incomingCall.reject(false, null); 226 // since the call was not added to the list of calls, we have to call the missed 227 // call notifier and the call logger manually. 228 mMissedCallNotifier.showMissedCallNotification(incomingCall); 229 mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE); 230 } else { 231 addCall(incomingCall); 232 } 233 } 234 235 @Override 236 public void onFailedIncomingCall(Call call) { 237 setCallState(call, CallState.DISCONNECTED); 238 call.removeListener(this); 239 } 240 241 @Override 242 public void onSuccessfulUnknownCall(Call call, int callState) { 243 setCallState(call, callState); 244 Log.i(this, "onSuccessfulUnknownCall for call %s", call); 245 addCall(call); 246 } 247 248 @Override 249 public void onFailedUnknownCall(Call call) { 250 Log.i(this, "onFailedUnknownCall for call %s", call); 251 setCallState(call, CallState.DISCONNECTED); 252 call.removeListener(this); 253 } 254 255 @Override 256 public void onRingbackRequested(Call call, boolean ringback) { 257 for (CallsManagerListener listener : mListeners) { 258 listener.onRingbackRequested(call, ringback); 259 } 260 } 261 262 @Override 263 public void onPostDialWait(Call call, String remaining) { 264 mInCallController.onPostDialWait(call, remaining); 265 } 266 267 @Override 268 public void onParentChanged(Call call) { 269 // parent-child relationship affects which call should be foreground, so do an update. 270 updateCallsManagerState(); 271 for (CallsManagerListener listener : mListeners) { 272 listener.onIsConferencedChanged(call); 273 } 274 } 275 276 @Override 277 public void onChildrenChanged(Call call) { 278 // parent-child relationship affects which call should be foreground, so do an update. 279 updateCallsManagerState(); 280 for (CallsManagerListener listener : mListeners) { 281 listener.onIsConferencedChanged(call); 282 } 283 } 284 285 @Override 286 public void onIsVoipAudioModeChanged(Call call) { 287 for (CallsManagerListener listener : mListeners) { 288 listener.onIsVoipAudioModeChanged(call); 289 } 290 } 291 292 @Override 293 public void onVideoStateChanged(Call call) { 294 for (CallsManagerListener listener : mListeners) { 295 listener.onVideoStateChanged(call); 296 } 297 } 298 299 @Override 300 public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call) { 301 mPendingCallsToDisconnect.add(call); 302 mHandler.postDelayed(new Runnable() { 303 @Override 304 public void run() { 305 if (mPendingCallsToDisconnect.remove(call)) { 306 Log.i(this, "Delayed disconnection of call: %s", call); 307 call.disconnect(); 308 } 309 } 310 }, Timeouts.getNewOutgoingCallCancelMillis(mContext.getContentResolver())); 311 312 return true; 313 } 314 315 Collection<Call> getCalls() { 316 return Collections.unmodifiableCollection(mCalls); 317 } 318 319 Call getForegroundCall() { 320 return mForegroundCall; 321 } 322 323 Ringer getRinger() { 324 return mRinger; 325 } 326 327 InCallController getInCallController() { 328 return mInCallController; 329 } 330 331 boolean hasEmergencyCall() { 332 for (Call call : mCalls) { 333 if (call.isEmergencyCall()) { 334 return true; 335 } 336 } 337 return false; 338 } 339 340 boolean hasVideoCall() { 341 for (Call call : mCalls) { 342 if (call.getVideoState() != VideoProfile.VideoState.AUDIO_ONLY) { 343 return true; 344 } 345 } 346 return false; 347 } 348 349 AudioState getAudioState() { 350 return mCallAudioManager.getAudioState(); 351 } 352 353 boolean isTtySupported() { 354 return mTtyManager.isTtySupported(); 355 } 356 357 int getCurrentTtyMode() { 358 return mTtyManager.getCurrentTtyMode(); 359 } 360 361 void addListener(CallsManagerListener listener) { 362 mListeners.add(listener); 363 } 364 365 void removeListener(CallsManagerListener listener) { 366 mListeners.remove(listener); 367 } 368 369 /** 370 * Starts the process to attach the call to a connection service. 371 * 372 * @param phoneAccountHandle The phone account which contains the component name of the 373 * connection service to use for this call. 374 * @param extras The optional extras Bundle passed with the intent used for the incoming call. 375 */ 376 void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 377 Log.d(this, "processIncomingCallIntent"); 378 Uri handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER); 379 Call call = new Call( 380 mContext, 381 mConnectionServiceRepository, 382 handle, 383 null /* gatewayInfo */, 384 null /* connectionManagerPhoneAccount */, 385 phoneAccountHandle, 386 true /* isIncoming */, 387 false /* isConference */); 388 389 call.setExtras(extras); 390 // TODO: Move this to be a part of addCall() 391 call.addListener(this); 392 call.startCreateConnection(mPhoneAccountRegistrar); 393 } 394 395 void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 396 Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE); 397 Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle)); 398 Call call = new Call( 399 mContext, 400 mConnectionServiceRepository, 401 handle, 402 null /* gatewayInfo */, 403 null /* connectionManagerPhoneAccount */, 404 phoneAccountHandle, 405 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach 406 // to the existing connection instead of trying to create a new one. 407 true /* isIncoming */, 408 false /* isConference */); 409 call.setConnectTimeMillis(System.currentTimeMillis()); 410 call.setIsUnknown(true); 411 call.setExtras(extras); 412 call.addListener(this); 413 call.startCreateConnection(mPhoneAccountRegistrar); 414 } 415 416 private Call getNewOutgoingCall(Uri handle) { 417 // First check to see if we can reuse any of the calls that are waiting to disconnect. 418 // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information. 419 for (Call pendingCall : mPendingCallsToDisconnect) { 420 if (Objects.equals(pendingCall.getHandle(), handle)) { 421 mPendingCallsToDisconnect.remove(pendingCall); 422 Log.i(this, "Reusing disconnected call %s", pendingCall); 423 return pendingCall; 424 } 425 } 426 427 // Create a call with original handle. The handle may be changed when the call is attached 428 // to a connection service, but in most cases will remain the same. 429 return new Call( 430 mContext, 431 mConnectionServiceRepository, 432 handle, 433 null /* gatewayInfo */, 434 null /* connectionManagerPhoneAccount */, 435 null /* phoneAccountHandle */, 436 false /* isIncoming */, 437 false /* isConference */); 438 } 439 440 /** 441 * Kicks off the first steps to creating an outgoing call so that InCallUI can launch. 442 * 443 * @param handle Handle to connect the call with. 444 * @param phoneAccountHandle The phone account which contains the component name of the 445 * connection service to use for this call. 446 * @param extras The optional extras Bundle passed with the intent used for the incoming call. 447 */ 448 Call startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras) { 449 Call call = getNewOutgoingCall(handle); 450 451 List<PhoneAccountHandle> accounts = 452 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme()); 453 454 Log.v(this, "startOutgoingCall found accounts = " + accounts); 455 456 // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this call 457 // as if a phoneAccount was not specified (does the default behavior instead). 458 // Note: We will not attempt to dial with a requested phoneAccount if it is disabled. 459 if (phoneAccountHandle != null) { 460 if (!accounts.contains(phoneAccountHandle)) { 461 phoneAccountHandle = null; 462 } 463 } 464 465 if (phoneAccountHandle == null) { 466 // No preset account, check if default exists that supports the URI scheme for the 467 // handle. 468 PhoneAccountHandle defaultAccountHandle = 469 mPhoneAccountRegistrar.getDefaultOutgoingPhoneAccount( 470 handle.getScheme()); 471 if (defaultAccountHandle != null) { 472 phoneAccountHandle = defaultAccountHandle; 473 } 474 } 475 476 call.setTargetPhoneAccount(phoneAccountHandle); 477 478 boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext, 479 call.getHandle()); 480 boolean isPotentialInCallMMICode = isPotentialInCallMMICode(handle); 481 482 // Do not support any more live calls. Our options are to move a call to hold, disconnect 483 // a call, or cancel this call altogether. 484 if (!isPotentialInCallMMICode && !makeRoomForOutgoingCall(call, isEmergencyCall)) { 485 // just cancel at this point. 486 if (mCalls.contains(call)) { 487 // This call can already exist if it is a reused call, 488 // See {@link #getNewOutgoingCall}. 489 call.disconnect(); 490 } 491 return null; 492 } 493 494 boolean needsAccountSelection = phoneAccountHandle == null && accounts.size() > 1 && 495 !isEmergencyCall; 496 497 if (needsAccountSelection) { 498 // This is the state where the user is expected to select an account 499 call.setState(CallState.PRE_DIAL_WAIT); 500 extras.putParcelableList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, accounts); 501 } else { 502 call.setState(CallState.CONNECTING); 503 } 504 505 call.setExtras(extras); 506 507 // Do not add the call if it is a potential MMI code. 508 if ((isPotentialMMICode(handle) || isPotentialInCallMMICode) && !needsAccountSelection) { 509 call.addListener(this); 510 } else if (!mCalls.contains(call)) { 511 // We check if mCalls already contains the call because we could potentially be reusing 512 // a call which was previously added (See {@link #getNewOutgoingCall}). 513 addCall(call); 514 } 515 516 return call; 517 } 518 519 /** 520 * Attempts to issue/connect the specified call. 521 * 522 * @param handle Handle to connect the call with. 523 * @param gatewayInfo Optional gateway information that can be used to route the call to the 524 * actual dialed handle via a gateway provider. May be null. 525 * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects. 526 * @param videoState The desired video state for the outgoing call. 527 */ 528 void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, 529 int videoState) { 530 if (call == null) { 531 // don't do anything if the call no longer exists 532 Log.i(this, "Canceling unknown call."); 533 return; 534 } 535 536 final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress(); 537 538 if (gatewayInfo == null) { 539 Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle)); 540 } else { 541 Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s", 542 Log.pii(uriHandle), Log.pii(handle)); 543 } 544 545 call.setHandle(uriHandle); 546 call.setGatewayInfo(gatewayInfo); 547 call.setStartWithSpeakerphoneOn(speakerphoneOn); 548 call.setVideoState(videoState); 549 550 boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext, 551 call.getHandle()); 552 if (isEmergencyCall) { 553 // Emergency -- CreateConnectionProcessor will choose accounts automatically 554 call.setTargetPhoneAccount(null); 555 } 556 557 if (call.getTargetPhoneAccount() != null || isEmergencyCall) { 558 // If the account has been set, proceed to place the outgoing call. 559 // Otherwise the connection will be initiated when the account is set by the user. 560 call.startCreateConnection(mPhoneAccountRegistrar); 561 } 562 } 563 564 /** 565 * Attempts to start a conference call for the specified call. 566 * 567 * @param call The call to conference. 568 * @param otherCall The other call to conference with. 569 */ 570 void conference(Call call, Call otherCall) { 571 call.conferenceWith(otherCall); 572 } 573 574 /** 575 * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call 576 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 577 * the user opting to answer said call. 578 * 579 * @param call The call to answer. 580 * @param videoState The video state in which to answer the call. 581 */ 582 void answerCall(Call call, int videoState) { 583 if (!mCalls.contains(call)) { 584 Log.i(this, "Request to answer a non-existent call %s", call); 585 } else { 586 // If the foreground call is not the ringing call and it is currently isActive() or 587 // STATE_DIALING, put it on hold before answering the call. 588 if (mForegroundCall != null && mForegroundCall != call && 589 (mForegroundCall.isActive() || 590 mForegroundCall.getState() == CallState.DIALING)) { 591 if (0 == (mForegroundCall.getCallCapabilities() & PhoneCapabilities.HOLD)) { 592 // This call does not support hold. If it is from a different connection 593 // service, then disconnect it, otherwise allow the connection service to 594 // figure out the right states. 595 if (mForegroundCall.getConnectionService() != call.getConnectionService()) { 596 mForegroundCall.disconnect(); 597 } 598 } else { 599 Log.v(this, "Holding active/dialing call %s before answering incoming call %s.", 600 mForegroundCall, call); 601 mForegroundCall.hold(); 602 } 603 // TODO: Wait until we get confirmation of the active call being 604 // on-hold before answering the new call. 605 // TODO: Import logic from CallManager.acceptCall() 606 } 607 608 for (CallsManagerListener listener : mListeners) { 609 listener.onIncomingCallAnswered(call); 610 } 611 612 // We do not update the UI until we get confirmation of the answer() through 613 // {@link #markCallAsActive}. 614 call.answer(videoState); 615 } 616 } 617 618 /** 619 * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call 620 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 621 * the user opting to reject said call. 622 */ 623 void rejectCall(Call call, boolean rejectWithMessage, String textMessage) { 624 if (!mCalls.contains(call)) { 625 Log.i(this, "Request to reject a non-existent call %s", call); 626 } else { 627 for (CallsManagerListener listener : mListeners) { 628 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage); 629 } 630 call.reject(rejectWithMessage, textMessage); 631 } 632 } 633 634 /** 635 * Instructs Telecom to play the specified DTMF tone within the specified call. 636 * 637 * @param digit The DTMF digit to play. 638 */ 639 void playDtmfTone(Call call, char digit) { 640 if (!mCalls.contains(call)) { 641 Log.i(this, "Request to play DTMF in a non-existent call %s", call); 642 } else { 643 call.playDtmfTone(digit); 644 mDtmfLocalTonePlayer.playTone(call, digit); 645 } 646 } 647 648 /** 649 * Instructs Telecom to stop the currently playing DTMF tone, if any. 650 */ 651 void stopDtmfTone(Call call) { 652 if (!mCalls.contains(call)) { 653 Log.i(this, "Request to stop DTMF in a non-existent call %s", call); 654 } else { 655 call.stopDtmfTone(); 656 mDtmfLocalTonePlayer.stopTone(call); 657 } 658 } 659 660 /** 661 * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any. 662 */ 663 void postDialContinue(Call call, boolean proceed) { 664 if (!mCalls.contains(call)) { 665 Log.i(this, "Request to continue post-dial string in a non-existent call %s", call); 666 } else { 667 call.postDialContinue(proceed); 668 } 669 } 670 671 /** 672 * Instructs Telecom to disconnect the specified call. Intended to be invoked by the 673 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 674 * the user hitting the end-call button. 675 */ 676 void disconnectCall(Call call) { 677 Log.v(this, "disconnectCall %s", call); 678 679 if (!mCalls.contains(call)) { 680 Log.w(this, "Unknown call (%s) asked to disconnect", call); 681 } else { 682 mLocallyDisconnectingCalls.add(call); 683 call.disconnect(); 684 } 685 } 686 687 /** 688 * Instructs Telecom to disconnect all calls. 689 */ 690 void disconnectAllCalls() { 691 Log.v(this, "disconnectAllCalls"); 692 693 for (Call call : mCalls) { 694 disconnectCall(call); 695 } 696 } 697 698 699 /** 700 * Instructs Telecom to put the specified call on hold. Intended to be invoked by the 701 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 702 * the user hitting the hold button during an active call. 703 */ 704 void holdCall(Call call) { 705 if (!mCalls.contains(call)) { 706 Log.w(this, "Unknown call (%s) asked to be put on hold", call); 707 } else { 708 Log.d(this, "Putting call on hold: (%s)", call); 709 call.hold(); 710 } 711 } 712 713 /** 714 * Instructs Telecom to release the specified call from hold. Intended to be invoked by 715 * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered 716 * by the user hitting the hold button during a held call. 717 */ 718 void unholdCall(Call call) { 719 if (!mCalls.contains(call)) { 720 Log.w(this, "Unknown call (%s) asked to be removed from hold", call); 721 } else { 722 Log.d(this, "unholding call: (%s)", call); 723 for (Call c : mCalls) { 724 if (c != null && c.isAlive() && c != call) { 725 c.hold(); 726 } 727 } 728 call.unhold(); 729 } 730 } 731 732 /** Called by the in-call UI to change the mute state. */ 733 void mute(boolean shouldMute) { 734 mCallAudioManager.mute(shouldMute); 735 } 736 737 /** 738 * Called by the in-call UI to change the audio route, for example to change from earpiece to 739 * speaker phone. 740 */ 741 void setAudioRoute(int route) { 742 mCallAudioManager.setAudioRoute(route); 743 } 744 745 /** Called by the in-call UI to turn the proximity sensor on. */ 746 void turnOnProximitySensor() { 747 mProximitySensorManager.turnOn(); 748 } 749 750 /** 751 * Called by the in-call UI to turn the proximity sensor off. 752 * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise, 753 * the screen will be kept off until the proximity sensor goes negative. 754 */ 755 void turnOffProximitySensor(boolean screenOnImmediately) { 756 mProximitySensorManager.turnOff(screenOnImmediately); 757 } 758 759 void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) { 760 if (!mCalls.contains(call)) { 761 Log.i(this, "Attempted to add account to unknown call %s", call); 762 } else { 763 // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and 764 // the PRE_DIAL_WAIT sequence run in parallel, if the user selects an account before the 765 // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without 766 // respecting a rewritten number or a canceled number. This is unlikely since 767 // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting 768 // a phone account from the in-call UI. 769 call.setTargetPhoneAccount(account); 770 771 // Note: emergency calls never go through account selection dialog so they never 772 // arrive here. 773 if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) { 774 call.startCreateConnection(mPhoneAccountRegistrar); 775 } else { 776 call.disconnect(); 777 } 778 779 if (setDefault) { 780 mPhoneAccountRegistrar.setUserSelectedOutgoingPhoneAccount(account); 781 } 782 } 783 } 784 785 /** Called when the audio state changes. */ 786 void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState) { 787 Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState); 788 for (CallsManagerListener listener : mListeners) { 789 listener.onAudioStateChanged(oldAudioState, newAudioState); 790 } 791 } 792 793 void markCallAsRinging(Call call) { 794 setCallState(call, CallState.RINGING); 795 } 796 797 void markCallAsDialing(Call call) { 798 setCallState(call, CallState.DIALING); 799 } 800 801 void markCallAsActive(Call call) { 802 if (call.getConnectTimeMillis() == 0) { 803 call.setConnectTimeMillis(System.currentTimeMillis()); 804 } 805 setCallState(call, CallState.ACTIVE); 806 807 if (call.getStartWithSpeakerphoneOn()) { 808 setAudioRoute(AudioState.ROUTE_SPEAKER); 809 } 810 } 811 812 void markCallAsOnHold(Call call) { 813 setCallState(call, CallState.ON_HOLD); 814 } 815 816 /** 817 * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the 818 * last live call, then also disconnect from the in-call controller. 819 * 820 * @param disconnectCause The disconnect cause, see {@link android.telecomm.DisconnectCause}. 821 */ 822 void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) { 823 call.setDisconnectCause(disconnectCause); 824 setCallState(call, CallState.DISCONNECTED); 825 } 826 827 /** 828 * Removes an existing disconnected call, and notifies the in-call app. 829 */ 830 void markCallAsRemoved(Call call) { 831 removeCall(call); 832 if (mLocallyDisconnectingCalls.contains(call)) { 833 mLocallyDisconnectingCalls.remove(call); 834 if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) { 835 mForegroundCall.unhold(); 836 } 837 } 838 } 839 840 /** 841 * Cleans up any calls currently associated with the specified connection service when the 842 * service binder disconnects unexpectedly. 843 * 844 * @param service The connection service that disconnected. 845 */ 846 void handleConnectionServiceDeath(ConnectionServiceWrapper service) { 847 if (service != null) { 848 for (Call call : mCalls) { 849 if (call.getConnectionService() == service) { 850 if (call.getState() != CallState.DISCONNECTED) { 851 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR)); 852 } 853 markCallAsRemoved(call); 854 } 855 } 856 } 857 } 858 859 boolean hasAnyCalls() { 860 return !mCalls.isEmpty(); 861 } 862 863 boolean hasActiveOrHoldingCall() { 864 return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null; 865 } 866 867 boolean hasRingingCall() { 868 return getFirstCallWithState(CallState.RINGING) != null; 869 } 870 871 boolean onMediaButton(int type) { 872 if (hasAnyCalls()) { 873 if (HeadsetMediaButton.SHORT_PRESS == type) { 874 Call ringingCall = getFirstCallWithState(CallState.RINGING); 875 if (ringingCall == null) { 876 mCallAudioManager.toggleMute(); 877 return true; 878 } else { 879 ringingCall.answer(ringingCall.getVideoState()); 880 return true; 881 } 882 } else if (HeadsetMediaButton.LONG_PRESS == type) { 883 Log.d(this, "handleHeadsetHook: longpress -> hangup"); 884 Call callToHangup = getFirstCallWithState( 885 CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD); 886 if (callToHangup != null) { 887 callToHangup.disconnect(); 888 return true; 889 } 890 } 891 } 892 return false; 893 } 894 895 /** 896 * Returns true if telecom supports adding another top-level call. 897 */ 898 boolean canAddCall() { 899 int count = 0; 900 for (Call call : mCalls) { 901 if (call.isEmergencyCall()) { 902 // We never support add call if one of the calls is an emergency call. 903 return false; 904 } else if (call.getParentCall() == null) { 905 count++; 906 } 907 908 // We do not check states for canAddCall. We treat disconnected calls the same 909 // and wait until they are removed instead. If we didn't count disconnected calls, 910 // we could put InCallServices into a state where they are showing two calls but 911 // also support add-call. Technically it's right, but overall looks better (UI-wise) 912 // and acts better if we wait until the call is removed. 913 if (count >= MAXIMUM_TOP_LEVEL_CALLS) { 914 return false; 915 } 916 } 917 return true; 918 } 919 920 Call getRingingCall() { 921 return getFirstCallWithState(CallState.RINGING); 922 } 923 924 Call getActiveCall() { 925 return getFirstCallWithState(CallState.ACTIVE); 926 } 927 928 Call getDialingCall() { 929 return getFirstCallWithState(CallState.DIALING); 930 } 931 932 Call getHeldCall() { 933 return getFirstCallWithState(CallState.ON_HOLD); 934 } 935 936 int getNumHeldCalls() { 937 int count = 0; 938 for (Call call : mCalls) { 939 if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) { 940 count++; 941 } 942 } 943 return count; 944 } 945 946 Call getFirstCallWithState(int... states) { 947 return getFirstCallWithState(null, states); 948 } 949 950 /** 951 * Returns the first call that it finds with the given states. The states are treated as having 952 * priority order so that any call with the first state will be returned before any call with 953 * states listed later in the parameter list. 954 * 955 * @param callToSkip Call that this method should skip while searching 956 */ 957 Call getFirstCallWithState(Call callToSkip, int... states) { 958 for (int currentState : states) { 959 // check the foreground first 960 if (mForegroundCall != null && mForegroundCall.getState() == currentState) { 961 return mForegroundCall; 962 } 963 964 for (Call call : mCalls) { 965 if (Objects.equals(callToSkip, call)) { 966 continue; 967 } 968 969 // Only operate on top-level calls 970 if (call.getParentCall() != null) { 971 continue; 972 } 973 974 if (currentState == call.getState()) { 975 return call; 976 } 977 } 978 } 979 return null; 980 } 981 982 Call createConferenceCall( 983 PhoneAccountHandle phoneAccount, 984 ParcelableConference parcelableConference) { 985 Call call = new Call( 986 mContext, 987 mConnectionServiceRepository, 988 null /* handle */, 989 null /* gatewayInfo */, 990 null /* connectionManagerPhoneAccount */, 991 phoneAccount, 992 false /* isIncoming */, 993 true /* isConference */); 994 995 setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState())); 996 if (call.getState() == CallState.ACTIVE) { 997 call.setConnectTimeMillis(System.currentTimeMillis()); 998 } 999 call.setCallCapabilities(parcelableConference.getCapabilities()); 1000 1001 // TODO: Move this to be a part of addCall() 1002 call.addListener(this); 1003 addCall(call); 1004 return call; 1005 } 1006 1007 /** 1008 * @return the call state currently tracked by {@link PhoneStateBroadcaster} 1009 */ 1010 int getCallState() { 1011 return mPhoneStateBroadcaster.getCallState(); 1012 } 1013 1014 /** 1015 * Retrieves the {@link PhoneAccountRegistrar}. 1016 * 1017 * @return The {@link PhoneAccountRegistrar}. 1018 */ 1019 PhoneAccountRegistrar getPhoneAccountRegistrar() { 1020 return mPhoneAccountRegistrar; 1021 } 1022 1023 /** 1024 * Retrieves the {@link MissedCallNotifier} 1025 * @return The {@link MissedCallNotifier}. 1026 */ 1027 MissedCallNotifier getMissedCallNotifier() { 1028 return mMissedCallNotifier; 1029 } 1030 1031 /** 1032 * Adds the specified call to the main list of live calls. 1033 * 1034 * @param call The call to add. 1035 */ 1036 private void addCall(Call call) { 1037 Trace.beginSection("addCall"); 1038 Log.v(this, "addCall(%s)", call); 1039 call.addListener(this); 1040 mCalls.add(call); 1041 1042 // TODO: Update mForegroundCall prior to invoking 1043 // onCallAdded for calls which immediately take the foreground (like the first call). 1044 for (CallsManagerListener listener : mListeners) { 1045 if (Log.SYSTRACE_DEBUG) { 1046 Trace.beginSection(listener.getClass().toString() + " addCall"); 1047 } 1048 listener.onCallAdded(call); 1049 if (Log.SYSTRACE_DEBUG) { 1050 Trace.endSection(); 1051 } 1052 } 1053 updateCallsManagerState(); 1054 Trace.endSection(); 1055 } 1056 1057 private void removeCall(Call call) { 1058 Trace.beginSection("removeCall"); 1059 Log.v(this, "removeCall(%s)", call); 1060 1061 call.setParentCall(null); // need to clean up parent relationship before destroying. 1062 call.removeListener(this); 1063 call.clearConnectionService(); 1064 1065 boolean shouldNotify = false; 1066 if (mCalls.contains(call)) { 1067 mCalls.remove(call); 1068 shouldNotify = true; 1069 } 1070 1071 // Only broadcast changes for calls that are being tracked. 1072 if (shouldNotify) { 1073 for (CallsManagerListener listener : mListeners) { 1074 if (Log.SYSTRACE_DEBUG) { 1075 Trace.beginSection(listener.getClass().toString() + " onCallRemoved"); 1076 } 1077 listener.onCallRemoved(call); 1078 if (Log.SYSTRACE_DEBUG) { 1079 Trace.endSection(); 1080 } 1081 } 1082 updateCallsManagerState(); 1083 } 1084 Trace.endSection(); 1085 } 1086 1087 /** 1088 * Sets the specified state on the specified call. 1089 * 1090 * @param call The call. 1091 * @param newState The new state of the call. 1092 */ 1093 private void setCallState(Call call, int newState) { 1094 if (call == null) { 1095 return; 1096 } 1097 int oldState = call.getState(); 1098 Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState), 1099 CallState.toString(newState), call); 1100 if (newState != oldState) { 1101 // Unfortunately, in the telephony world the radio is king. So if the call notifies 1102 // us that the call is in a particular state, we allow it even if it doesn't make 1103 // sense (e.g., STATE_ACTIVE -> STATE_RINGING). 1104 // TODO: Consider putting a stop to the above and turning CallState 1105 // into a well-defined state machine. 1106 // TODO: Define expected state transitions here, and log when an 1107 // unexpected transition occurs. 1108 call.setState(newState); 1109 1110 Trace.beginSection("onCallStateChanged"); 1111 // Only broadcast state change for calls that are being tracked. 1112 if (mCalls.contains(call)) { 1113 for (CallsManagerListener listener : mListeners) { 1114 if (Log.SYSTRACE_DEBUG) { 1115 Trace.beginSection(listener.getClass().toString() + " onCallStateChanged"); 1116 } 1117 listener.onCallStateChanged(call, oldState, newState); 1118 if (Log.SYSTRACE_DEBUG) { 1119 Trace.endSection(); 1120 } 1121 } 1122 updateCallsManagerState(); 1123 } 1124 Trace.endSection(); 1125 } 1126 } 1127 1128 /** 1129 * Checks which call should be visible to the user and have audio focus. 1130 */ 1131 private void updateForegroundCall() { 1132 Trace.beginSection("updateForegroundCall"); 1133 Call newForegroundCall = null; 1134 for (Call call : mCalls) { 1135 // TODO: Foreground-ness needs to be explicitly set. No call, regardless 1136 // of its state will be foreground by default and instead the connection service should 1137 // be notified when its calls enter and exit foreground state. Foreground will mean that 1138 // the call should play audio and listen to microphone if it wants. 1139 1140 // Only top-level calls can be in foreground 1141 if (call.getParentCall() != null) { 1142 continue; 1143 } 1144 1145 // Active calls have priority. 1146 if (call.isActive()) { 1147 newForegroundCall = call; 1148 break; 1149 } 1150 1151 if (call.isAlive() || call.getState() == CallState.RINGING) { 1152 newForegroundCall = call; 1153 // Don't break in case there's an active call that has priority. 1154 } 1155 } 1156 1157 if (newForegroundCall != mForegroundCall) { 1158 Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall); 1159 Call oldForegroundCall = mForegroundCall; 1160 mForegroundCall = newForegroundCall; 1161 1162 for (CallsManagerListener listener : mListeners) { 1163 if (Log.SYSTRACE_DEBUG) { 1164 Trace.beginSection(listener.getClass().toString() + " updateForegroundCall"); 1165 } 1166 listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall); 1167 if (Log.SYSTRACE_DEBUG) { 1168 Trace.endSection(); 1169 } 1170 } 1171 } 1172 Trace.endSection(); 1173 } 1174 1175 private void updateCanAddCall() { 1176 boolean newCanAddCall = canAddCall(); 1177 if (newCanAddCall != mCanAddCall) { 1178 mCanAddCall = newCanAddCall; 1179 for (CallsManagerListener listener : mListeners) { 1180 if (Log.SYSTRACE_DEBUG) { 1181 Trace.beginSection(listener.getClass().toString() + " updateCanAddCall"); 1182 } 1183 listener.onCanAddCallChanged(mCanAddCall); 1184 if (Log.SYSTRACE_DEBUG) { 1185 Trace.endSection(); 1186 } 1187 } 1188 } 1189 } 1190 1191 private void updateCallsManagerState() { 1192 updateForegroundCall(); 1193 updateCanAddCall(); 1194 } 1195 1196 private boolean isPotentialMMICode(Uri handle) { 1197 return (handle != null && handle.getSchemeSpecificPart() != null 1198 && handle.getSchemeSpecificPart().contains("#")); 1199 } 1200 1201 /** 1202 * Determines if a dialed number is potentially an In-Call MMI code. In-Call MMI codes are 1203 * MMI codes which can be dialed when one or more calls are in progress. 1204 * <P> 1205 * Checks for numbers formatted similar to the MMI codes defined in: 1206 * {@link com.android.internal.telephony.gsm.GSMPhone#handleInCallMmiCommands(String)} 1207 * and 1208 * {@link com.android.internal.telephony.imsphone.ImsPhone#handleInCallMmiCommands(String)} 1209 * 1210 * @param handle The URI to call. 1211 * @return {@code True} if the URI represents a number which could be an in-call MMI code. 1212 */ 1213 private boolean isPotentialInCallMMICode(Uri handle) { 1214 if (handle != null && handle.getSchemeSpecificPart() != null && 1215 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) { 1216 1217 String dialedNumber = handle.getSchemeSpecificPart(); 1218 return (dialedNumber.equals("0") || 1219 (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) || 1220 (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) || 1221 dialedNumber.equals("3") || 1222 dialedNumber.equals("4") || 1223 dialedNumber.equals("5")); 1224 } 1225 return false; 1226 } 1227 1228 private int getNumCallsWithState(int... states) { 1229 int count = 0; 1230 for (int state : states) { 1231 for (Call call : mCalls) { 1232 if (call.getParentCall() == null && call.getState() == state) { 1233 count++; 1234 } 1235 } 1236 } 1237 return count; 1238 } 1239 1240 private boolean hasMaximumLiveCalls() { 1241 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES); 1242 } 1243 1244 private boolean hasMaximumHoldingCalls() { 1245 return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD); 1246 } 1247 1248 private boolean hasMaximumRingingCalls() { 1249 return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING); 1250 } 1251 1252 private boolean hasMaximumOutgoingCalls() { 1253 return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES); 1254 } 1255 1256 private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) { 1257 if (hasMaximumLiveCalls()) { 1258 // NOTE: If the amount of live calls changes beyond 1, this logic will probably 1259 // have to change. 1260 Call liveCall = getFirstCallWithState(call, LIVE_CALL_STATES); 1261 1262 if (call == liveCall) { 1263 // If the call is already the foreground call, then we are golden. 1264 // This can happen after the user selects an account in the PRE_DIAL_WAIT 1265 // state since the call was already populated into the list. 1266 return true; 1267 } 1268 1269 if (hasMaximumOutgoingCalls()) { 1270 // Disconnect the current outgoing call if it's not an emergency call. If the user 1271 // tries to make two outgoing calls to different emergency call numbers, we will try 1272 // to connect the first outgoing call. 1273 if (isEmergency) { 1274 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES); 1275 if (!outgoingCall.isEmergencyCall()) { 1276 outgoingCall.disconnect(); 1277 return true; 1278 } 1279 } 1280 return false; 1281 } 1282 1283 if (hasMaximumHoldingCalls()) { 1284 // There is no more room for any more calls, unless it's an emergency. 1285 if (isEmergency) { 1286 // Kill the current active call, this is easier then trying to disconnect a 1287 // holding call and hold an active call. 1288 liveCall.disconnect(); 1289 return true; 1290 } 1291 return false; // No more room! 1292 } 1293 1294 // We have room for at least one more holding call at this point. 1295 1296 // First thing, if we are trying to make a call with the same phone account as the live 1297 // call, then allow it so that the connection service can make its own decision about 1298 // how to handle the new call relative to the current one. 1299 if (Objects.equals(liveCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { 1300 return true; 1301 } else if (call.getTargetPhoneAccount() == null) { 1302 // Without a phone account, we can't say reliably that the call will fail. 1303 // If the user chooses the same phone account as the live call, then it's 1304 // still possible that the call can be made (like with CDMA calls not supporting 1305 // hold but they still support adding a call by going immediately into conference 1306 // mode). Return true here and we'll run this code again after user chooses an 1307 // account. 1308 return true; 1309 } 1310 1311 // Try to hold the live call before attempting the new outgoing call. 1312 if (liveCall.can(PhoneCapabilities.HOLD)) { 1313 liveCall.hold(); 1314 return true; 1315 } 1316 1317 // The live call cannot be held so we're out of luck here. There's no room. 1318 return false; 1319 } 1320 return true; 1321 } 1322 1323 /** 1324 * Creates a new call for an existing connection. 1325 * 1326 * @param callId The id of the new call. 1327 * @param connection The connection information. 1328 * @return The new call. 1329 */ 1330 Call createCallForExistingConnection(String callId, ParcelableConnection connection) { 1331 Call call = new Call( 1332 mContext, 1333 mConnectionServiceRepository, 1334 connection.getHandle() /* handle */, 1335 null /* gatewayInfo */, 1336 null /* connectionManagerPhoneAccount */, 1337 connection.getPhoneAccount(), /* targetPhoneAccountHandle */ 1338 false /* isIncoming */, 1339 false /* isConference */); 1340 1341 setCallState(call, Call.getStateFromConnectionState(connection.getState())); 1342 call.setConnectTimeMillis(System.currentTimeMillis()); 1343 call.setCallCapabilities(connection.getCapabilities()); 1344 call.setCallerDisplayName(connection.getCallerDisplayName(), 1345 connection.getCallerDisplayNamePresentation()); 1346 1347 call.addListener(this); 1348 addCall(call); 1349 1350 return call; 1351 } 1352 1353 /** 1354 * Dumps the state of the {@link CallsManager}. 1355 * 1356 * @param pw The {@code IndentingPrintWriter} to write the state to. 1357 */ 1358 public void dump(IndentingPrintWriter pw) { 1359 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 1360 if (mCalls != null) { 1361 pw.println("mCalls: "); 1362 pw.increaseIndent(); 1363 for (Call call : mCalls) { 1364 pw.println(call); 1365 } 1366 pw.decreaseIndent(); 1367 } 1368 pw.println("mForegroundCall: " + (mForegroundCall == null ? "none" : mForegroundCall)); 1369 1370 if (mCallAudioManager != null) { 1371 pw.println("mCallAudioManager:"); 1372 pw.increaseIndent(); 1373 mCallAudioManager.dump(pw); 1374 pw.decreaseIndent(); 1375 } 1376 1377 if (mTtyManager != null) { 1378 pw.println("mTtyManager:"); 1379 pw.increaseIndent(); 1380 mTtyManager.dump(pw); 1381 pw.decreaseIndent(); 1382 } 1383 1384 if (mInCallController != null) { 1385 pw.println("mInCallController:"); 1386 pw.increaseIndent(); 1387 mInCallController.dump(pw); 1388 pw.decreaseIndent(); 1389 } 1390 1391 if (mConnectionServiceRepository != null) { 1392 pw.println("mConnectionServiceRepository:"); 1393 pw.increaseIndent(); 1394 mConnectionServiceRepository.dump(pw); 1395 pw.decreaseIndent(); 1396 } 1397 } 1398} 1399