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