1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.telecom; 18 19 import android.content.Context; 20 import android.content.Intent; 21 import android.content.pm.PackageManager; 22 import android.content.pm.ResolveInfo; 23 import android.net.Uri; 24 import android.os.Bundle; 25 import android.provider.CallLog.Calls; 26 import android.telecom.AudioState; 27 import android.telecom.CallState; 28 import android.telecom.DisconnectCause; 29 import android.telecom.GatewayInfo; 30 import android.telecom.ParcelableConference; 31 import android.telecom.PhoneAccount; 32 import android.telecom.PhoneAccountHandle; 33 import android.telecom.PhoneCapabilities; 34 import android.telecom.TelecomManager; 35 import android.telephony.TelephonyManager; 36 37 import com.android.internal.util.IndentingPrintWriter; 38 39 import com.google.common.collect.ImmutableCollection; 40 import com.google.common.collect.ImmutableList; 41 42 import java.util.Collections; 43 import java.util.HashSet; 44 import java.util.List; 45 import java.util.Objects; 46 import java.util.Set; 47 import 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 */ 56 public final class CallsManager extends Call.ListenerBase { 57 58 // TODO: Consider renaming this CallsManagerPlugin. 59 interface CallsManagerListener { onCallAdded(Call call)60 void onCallAdded(Call call); onCallRemoved(Call call)61 void onCallRemoved(Call call); onCallStateChanged(Call call, int oldState, int newState)62 void onCallStateChanged(Call call, int oldState, int newState); onConnectionServiceChanged( Call call, ConnectionServiceWrapper oldService, ConnectionServiceWrapper newService)63 void onConnectionServiceChanged( 64 Call call, 65 ConnectionServiceWrapper oldService, 66 ConnectionServiceWrapper newService); onIncomingCallAnswered(Call call)67 void onIncomingCallAnswered(Call call); onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage)68 void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage); onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall)69 void onForegroundCallChanged(Call oldForegroundCall, Call newForegroundCall); onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState)70 void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState); onRingbackRequested(Call call, boolean ringback)71 void onRingbackRequested(Call call, boolean ringback); onIsConferencedChanged(Call call)72 void onIsConferencedChanged(Call call); onIsVoipAudioModeChanged(Call call)73 void onIsVoipAudioModeChanged(Call call); onVideoStateChanged(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. */ getInstance()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 */ initialize(CallsManager instance)142 static void initialize(CallsManager instance) { 143 INSTANCE = instance; 144 } 145 146 /** 147 * Initializes the required Telecom components. 148 */ CallsManager(Context context, MissedCallNotifier missedCallNotifier, PhoneAccountRegistrar phoneAccountRegistrar)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 onSuccessfulOutgoingCall(Call call, int callState)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 onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)204 public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) { 205 Log.v(this, "onFailedOutgoingCall, call: %s", call); 206 207 markCallAsRemoved(call); 208 } 209 210 @Override onSuccessfulIncomingCall(Call incomingCall)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 onFailedIncomingCall(Call call)227 public void onFailedIncomingCall(Call call) { 228 setCallState(call, CallState.DISCONNECTED); 229 call.removeListener(this); 230 } 231 232 @Override onSuccessfulUnknownCall(Call call, int callState)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 onFailedUnknownCall(Call call)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 onRingbackRequested(Call call, boolean ringback)247 public void onRingbackRequested(Call call, boolean ringback) { 248 for (CallsManagerListener listener : mListeners) { 249 listener.onRingbackRequested(call, ringback); 250 } 251 } 252 253 @Override onPostDialWait(Call call, String remaining)254 public void onPostDialWait(Call call, String remaining) { 255 mInCallController.onPostDialWait(call, remaining); 256 } 257 258 @Override onParentChanged(Call call)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 onChildrenChanged(Call call)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 onIsVoipAudioModeChanged(Call call)277 public void onIsVoipAudioModeChanged(Call call) { 278 for (CallsManagerListener listener : mListeners) { 279 listener.onIsVoipAudioModeChanged(call); 280 } 281 } 282 283 @Override onVideoStateChanged(Call call)284 public void onVideoStateChanged(Call call) { 285 for (CallsManagerListener listener : mListeners) { 286 listener.onVideoStateChanged(call); 287 } 288 } 289 getCalls()290 ImmutableCollection<Call> getCalls() { 291 return ImmutableList.copyOf(mCalls); 292 } 293 getForegroundCall()294 Call getForegroundCall() { 295 return mForegroundCall; 296 } 297 getRinger()298 Ringer getRinger() { 299 return mRinger; 300 } 301 getInCallController()302 InCallController getInCallController() { 303 return mInCallController; 304 } 305 hasEmergencyCall()306 boolean hasEmergencyCall() { 307 for (Call call : mCalls) { 308 if (call.isEmergencyCall()) { 309 return true; 310 } 311 } 312 return false; 313 } 314 getAudioState()315 AudioState getAudioState() { 316 return mCallAudioManager.getAudioState(); 317 } 318 isTtySupported()319 boolean isTtySupported() { 320 return mTtyManager.isTtySupported(); 321 } 322 getCurrentTtyMode()323 int getCurrentTtyMode() { 324 return mTtyManager.getCurrentTtyMode(); 325 } 326 addListener(CallsManagerListener listener)327 void addListener(CallsManagerListener listener) { 328 mListeners.add(listener); 329 } 330 removeListener(CallsManagerListener listener)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 */ processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras)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 addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras)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 */ startOutgoingCall(Uri handle, PhoneAccountHandle phoneAccountHandle, Bundle extras)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 if (isPotentialMMICode(handle) || isPotentialInCallMMICode) { 451 call.addListener(this); 452 } else { 453 addCall(call); 454 } 455 456 return call; 457 } 458 459 /** 460 * Attempts to issue/connect the specified call. 461 * 462 * @param handle Handle to connect the call with. 463 * @param gatewayInfo Optional gateway information that can be used to route the call to the 464 * actual dialed handle via a gateway provider. May be null. 465 * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects. 466 * @param videoState The desired video state for the outgoing call. 467 */ placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, int videoState)468 void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, boolean speakerphoneOn, 469 int videoState) { 470 if (call == null) { 471 // don't do anything if the call no longer exists 472 Log.i(this, "Canceling unknown call."); 473 return; 474 } 475 476 final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress(); 477 478 if (gatewayInfo == null) { 479 Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle)); 480 } else { 481 Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s", 482 Log.pii(uriHandle), Log.pii(handle)); 483 } 484 485 call.setHandle(uriHandle); 486 call.setGatewayInfo(gatewayInfo); 487 call.setStartWithSpeakerphoneOn(speakerphoneOn); 488 call.setVideoState(videoState); 489 490 boolean isEmergencyCall = TelephonyUtil.shouldProcessAsEmergency(mContext, 491 call.getHandle()); 492 if (isEmergencyCall) { 493 // Emergency -- CreateConnectionProcessor will choose accounts automatically 494 call.setTargetPhoneAccount(null); 495 } 496 497 if (call.getTargetPhoneAccount() != null || isEmergencyCall) { 498 // If the account has been set, proceed to place the outgoing call. 499 // Otherwise the connection will be initiated when the account is set by the user. 500 call.startCreateConnection(mPhoneAccountRegistrar); 501 } 502 } 503 504 /** 505 * Attempts to start a conference call for the specified call. 506 * 507 * @param call The call to conference. 508 * @param otherCall The other call to conference with. 509 */ conference(Call call, Call otherCall)510 void conference(Call call, Call otherCall) { 511 call.conferenceWith(otherCall); 512 } 513 514 /** 515 * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call 516 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 517 * the user opting to answer said call. 518 * 519 * @param call The call to answer. 520 * @param videoState The video state in which to answer the call. 521 */ answerCall(Call call, int videoState)522 void answerCall(Call call, int videoState) { 523 if (!mCalls.contains(call)) { 524 Log.i(this, "Request to answer a non-existent call %s", call); 525 } else { 526 // If the foreground call is not the ringing call and it is currently isActive() or 527 // STATE_DIALING, put it on hold before answering the call. 528 if (mForegroundCall != null && mForegroundCall != call && 529 (mForegroundCall.isActive() || 530 mForegroundCall.getState() == CallState.DIALING)) { 531 if (0 == (mForegroundCall.getCallCapabilities() & PhoneCapabilities.HOLD)) { 532 // This call does not support hold. If it is from a different connection 533 // service, then disconnect it, otherwise allow the connection service to 534 // figure out the right states. 535 if (mForegroundCall.getConnectionService() != call.getConnectionService()) { 536 mForegroundCall.disconnect(); 537 } 538 } else { 539 Log.v(this, "Holding active/dialing call %s before answering incoming call %s.", 540 mForegroundCall, call); 541 mForegroundCall.hold(); 542 } 543 // TODO: Wait until we get confirmation of the active call being 544 // on-hold before answering the new call. 545 // TODO: Import logic from CallManager.acceptCall() 546 } 547 548 for (CallsManagerListener listener : mListeners) { 549 listener.onIncomingCallAnswered(call); 550 } 551 552 // We do not update the UI until we get confirmation of the answer() through 553 // {@link #markCallAsActive}. 554 call.answer(videoState); 555 } 556 } 557 558 /** 559 * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call 560 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 561 * the user opting to reject said call. 562 */ rejectCall(Call call, boolean rejectWithMessage, String textMessage)563 void rejectCall(Call call, boolean rejectWithMessage, String textMessage) { 564 if (!mCalls.contains(call)) { 565 Log.i(this, "Request to reject a non-existent call %s", call); 566 } else { 567 for (CallsManagerListener listener : mListeners) { 568 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage); 569 } 570 call.reject(rejectWithMessage, textMessage); 571 } 572 } 573 574 /** 575 * Instructs Telecom to play the specified DTMF tone within the specified call. 576 * 577 * @param digit The DTMF digit to play. 578 */ playDtmfTone(Call call, char digit)579 void playDtmfTone(Call call, char digit) { 580 if (!mCalls.contains(call)) { 581 Log.i(this, "Request to play DTMF in a non-existent call %s", call); 582 } else { 583 call.playDtmfTone(digit); 584 mDtmfLocalTonePlayer.playTone(call, digit); 585 } 586 } 587 588 /** 589 * Instructs Telecom to stop the currently playing DTMF tone, if any. 590 */ stopDtmfTone(Call call)591 void stopDtmfTone(Call call) { 592 if (!mCalls.contains(call)) { 593 Log.i(this, "Request to stop DTMF in a non-existent call %s", call); 594 } else { 595 call.stopDtmfTone(); 596 mDtmfLocalTonePlayer.stopTone(call); 597 } 598 } 599 600 /** 601 * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any. 602 */ postDialContinue(Call call, boolean proceed)603 void postDialContinue(Call call, boolean proceed) { 604 if (!mCalls.contains(call)) { 605 Log.i(this, "Request to continue post-dial string in a non-existent call %s", call); 606 } else { 607 call.postDialContinue(proceed); 608 } 609 } 610 611 /** 612 * Instructs Telecom to disconnect the specified call. Intended to be invoked by the 613 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 614 * the user hitting the end-call button. 615 */ disconnectCall(Call call)616 void disconnectCall(Call call) { 617 Log.v(this, "disconnectCall %s", call); 618 619 if (!mCalls.contains(call)) { 620 Log.w(this, "Unknown call (%s) asked to disconnect", call); 621 } else { 622 mLocallyDisconnectingCalls.add(call); 623 call.disconnect(); 624 } 625 } 626 627 /** 628 * Instructs Telecom to disconnect all calls. 629 */ disconnectAllCalls()630 void disconnectAllCalls() { 631 Log.v(this, "disconnectAllCalls"); 632 633 for (Call call : mCalls) { 634 disconnectCall(call); 635 } 636 } 637 638 639 /** 640 * Instructs Telecom to put the specified call on hold. Intended to be invoked by the 641 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 642 * the user hitting the hold button during an active call. 643 */ holdCall(Call call)644 void holdCall(Call call) { 645 if (!mCalls.contains(call)) { 646 Log.w(this, "Unknown call (%s) asked to be put on hold", call); 647 } else { 648 Log.d(this, "Putting call on hold: (%s)", call); 649 call.hold(); 650 } 651 } 652 653 /** 654 * Instructs Telecom to release the specified call from hold. Intended to be invoked by 655 * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered 656 * by the user hitting the hold button during a held call. 657 */ unholdCall(Call call)658 void unholdCall(Call call) { 659 if (!mCalls.contains(call)) { 660 Log.w(this, "Unknown call (%s) asked to be removed from hold", call); 661 } else { 662 Log.d(this, "unholding call: (%s)", call); 663 for (Call c : mCalls) { 664 if (c != null && c.isAlive() && c != call) { 665 c.hold(); 666 } 667 } 668 call.unhold(); 669 } 670 } 671 672 /** Called by the in-call UI to change the mute state. */ mute(boolean shouldMute)673 void mute(boolean shouldMute) { 674 mCallAudioManager.mute(shouldMute); 675 } 676 677 /** 678 * Called by the in-call UI to change the audio route, for example to change from earpiece to 679 * speaker phone. 680 */ setAudioRoute(int route)681 void setAudioRoute(int route) { 682 mCallAudioManager.setAudioRoute(route); 683 } 684 685 /** Called by the in-call UI to turn the proximity sensor on. */ turnOnProximitySensor()686 void turnOnProximitySensor() { 687 mProximitySensorManager.turnOn(); 688 } 689 690 /** 691 * Called by the in-call UI to turn the proximity sensor off. 692 * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise, 693 * the screen will be kept off until the proximity sensor goes negative. 694 */ turnOffProximitySensor(boolean screenOnImmediately)695 void turnOffProximitySensor(boolean screenOnImmediately) { 696 mProximitySensorManager.turnOff(screenOnImmediately); 697 } 698 phoneAccountSelected(Call call, PhoneAccountHandle account)699 void phoneAccountSelected(Call call, PhoneAccountHandle account) { 700 if (!mCalls.contains(call)) { 701 Log.i(this, "Attempted to add account to unknown call %s", call); 702 } else { 703 // TODO: There is an odd race condition here. Since NewOutgoingCallIntentBroadcaster and 704 // the PRE_DIAL_WAIT sequence run in parallel, if the user selects an account before the 705 // NEW_OUTGOING_CALL sequence finishes, we'll start the call immediately without 706 // respecting a rewritten number or a canceled number. This is unlikely since 707 // NEW_OUTGOING_CALL sequence, in practice, runs a lot faster than the user selecting 708 // a phone account from the in-call UI. 709 call.setTargetPhoneAccount(account); 710 711 // Note: emergency calls never go through account selection dialog so they never 712 // arrive here. 713 if (makeRoomForOutgoingCall(call, false /* isEmergencyCall */)) { 714 call.startCreateConnection(mPhoneAccountRegistrar); 715 } else { 716 call.disconnect(); 717 } 718 } 719 } 720 721 /** Called when the audio state changes. */ onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState)722 void onAudioStateChanged(AudioState oldAudioState, AudioState newAudioState) { 723 Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState); 724 for (CallsManagerListener listener : mListeners) { 725 listener.onAudioStateChanged(oldAudioState, newAudioState); 726 } 727 } 728 markCallAsRinging(Call call)729 void markCallAsRinging(Call call) { 730 setCallState(call, CallState.RINGING); 731 } 732 markCallAsDialing(Call call)733 void markCallAsDialing(Call call) { 734 setCallState(call, CallState.DIALING); 735 } 736 markCallAsActive(Call call)737 void markCallAsActive(Call call) { 738 if (call.getConnectTimeMillis() == 0) { 739 call.setConnectTimeMillis(System.currentTimeMillis()); 740 } 741 setCallState(call, CallState.ACTIVE); 742 743 if (call.getStartWithSpeakerphoneOn()) { 744 setAudioRoute(AudioState.ROUTE_SPEAKER); 745 } 746 } 747 markCallAsOnHold(Call call)748 void markCallAsOnHold(Call call) { 749 setCallState(call, CallState.ON_HOLD); 750 } 751 752 /** 753 * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the 754 * last live call, then also disconnect from the in-call controller. 755 * 756 * @param disconnectCause The disconnect cause, see {@link android.telecomm.DisconnectCause}. 757 */ markCallAsDisconnected(Call call, DisconnectCause disconnectCause)758 void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) { 759 call.setDisconnectCause(disconnectCause); 760 setCallState(call, CallState.DISCONNECTED); 761 } 762 763 /** 764 * Removes an existing disconnected call, and notifies the in-call app. 765 */ markCallAsRemoved(Call call)766 void markCallAsRemoved(Call call) { 767 removeCall(call); 768 if (mLocallyDisconnectingCalls.contains(call)) { 769 mLocallyDisconnectingCalls.remove(call); 770 if (mForegroundCall != null && mForegroundCall.getState() == CallState.ON_HOLD) { 771 mForegroundCall.unhold(); 772 } 773 } 774 } 775 776 /** 777 * Cleans up any calls currently associated with the specified connection service when the 778 * service binder disconnects unexpectedly. 779 * 780 * @param service The connection service that disconnected. 781 */ handleConnectionServiceDeath(ConnectionServiceWrapper service)782 void handleConnectionServiceDeath(ConnectionServiceWrapper service) { 783 if (service != null) { 784 for (Call call : mCalls) { 785 if (call.getConnectionService() == service) { 786 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR)); 787 } 788 } 789 } 790 } 791 hasAnyCalls()792 boolean hasAnyCalls() { 793 return !mCalls.isEmpty(); 794 } 795 hasActiveOrHoldingCall()796 boolean hasActiveOrHoldingCall() { 797 return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null; 798 } 799 hasRingingCall()800 boolean hasRingingCall() { 801 return getFirstCallWithState(CallState.RINGING) != null; 802 } 803 onMediaButton(int type)804 boolean onMediaButton(int type) { 805 if (hasAnyCalls()) { 806 if (HeadsetMediaButton.SHORT_PRESS == type) { 807 Call ringingCall = getFirstCallWithState(CallState.RINGING); 808 if (ringingCall == null) { 809 mCallAudioManager.toggleMute(); 810 return true; 811 } else { 812 ringingCall.answer(ringingCall.getVideoState()); 813 return true; 814 } 815 } else if (HeadsetMediaButton.LONG_PRESS == type) { 816 Log.d(this, "handleHeadsetHook: longpress -> hangup"); 817 Call callToHangup = getFirstCallWithState( 818 CallState.RINGING, CallState.DIALING, CallState.ACTIVE, CallState.ON_HOLD); 819 if (callToHangup != null) { 820 callToHangup.disconnect(); 821 return true; 822 } 823 } 824 } 825 return false; 826 } 827 828 /** 829 * Checks to see if the specified call is the only high-level call and if so, enable the 830 * "Add-call" button. We allow you to add a second call but not a third or beyond. 831 * 832 * @param call The call to test for add-call. 833 * @return Whether the add-call feature should be enabled for the call. 834 */ isAddCallCapable(Call call)835 protected boolean isAddCallCapable(Call call) { 836 if (call.getParentCall() != null) { 837 // Never true for child calls. 838 return false; 839 } 840 841 // Use canManageConference as a mechanism to check if the call is CDMA. 842 // Disable "Add Call" for CDMA calls which are conference calls. 843 boolean canManageConference = PhoneCapabilities.MANAGE_CONFERENCE 844 == (call.getCallCapabilities() & PhoneCapabilities.MANAGE_CONFERENCE); 845 if (call.isConference() && !canManageConference) { 846 return false; 847 } 848 849 // Loop through all the other calls and there exists a top level (has no parent) call 850 // that is not the specified call, return false. 851 for (Call otherCall : mCalls) { 852 if (call != otherCall && otherCall.getParentCall() == null) { 853 return false; 854 } 855 } 856 return true; 857 } 858 getRingingCall()859 Call getRingingCall() { 860 return getFirstCallWithState(CallState.RINGING); 861 } 862 getActiveCall()863 Call getActiveCall() { 864 return getFirstCallWithState(CallState.ACTIVE); 865 } 866 getDialingCall()867 Call getDialingCall() { 868 return getFirstCallWithState(CallState.DIALING); 869 } 870 getHeldCall()871 Call getHeldCall() { 872 return getFirstCallWithState(CallState.ON_HOLD); 873 } 874 getNumHeldCalls()875 int getNumHeldCalls() { 876 int count = 0; 877 for (Call call : mCalls) { 878 if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) { 879 count++; 880 } 881 } 882 return count; 883 } 884 getFirstCallWithState(int... states)885 Call getFirstCallWithState(int... states) { 886 return getFirstCallWithState(null, states); 887 } 888 889 /** 890 * Returns the first call that it finds with the given states. The states are treated as having 891 * priority order so that any call with the first state will be returned before any call with 892 * states listed later in the parameter list. 893 * 894 * @param callToSkip Call that this method should skip while searching 895 */ getFirstCallWithState(Call callToSkip, int... states)896 Call getFirstCallWithState(Call callToSkip, int... states) { 897 for (int currentState : states) { 898 // check the foreground first 899 if (mForegroundCall != null && mForegroundCall.getState() == currentState) { 900 return mForegroundCall; 901 } 902 903 for (Call call : mCalls) { 904 if (Objects.equals(callToSkip, call)) { 905 continue; 906 } 907 908 // Only operate on top-level calls 909 if (call.getParentCall() != null) { 910 continue; 911 } 912 913 if (currentState == call.getState()) { 914 return call; 915 } 916 } 917 } 918 return null; 919 } 920 createConferenceCall( PhoneAccountHandle phoneAccount, ParcelableConference parcelableConference)921 Call createConferenceCall( 922 PhoneAccountHandle phoneAccount, 923 ParcelableConference parcelableConference) { 924 Call call = new Call( 925 mContext, 926 mConnectionServiceRepository, 927 null /* handle */, 928 null /* gatewayInfo */, 929 null /* connectionManagerPhoneAccount */, 930 phoneAccount, 931 false /* isIncoming */, 932 true /* isConference */); 933 934 setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState())); 935 if (call.getState() == CallState.ACTIVE) { 936 call.setConnectTimeMillis(System.currentTimeMillis()); 937 } 938 call.setCallCapabilities(parcelableConference.getCapabilities()); 939 940 // TODO: Move this to be a part of addCall() 941 call.addListener(this); 942 addCall(call); 943 return call; 944 } 945 946 /** 947 * @return the call state currently tracked by {@link PhoneStateBroadcaster} 948 */ getCallState()949 int getCallState() { 950 return mPhoneStateBroadcaster.getCallState(); 951 } 952 953 /** 954 * Retrieves the {@link PhoneAccountRegistrar}. 955 * 956 * @return The {@link PhoneAccountRegistrar}. 957 */ getPhoneAccountRegistrar()958 PhoneAccountRegistrar getPhoneAccountRegistrar() { 959 return mPhoneAccountRegistrar; 960 } 961 962 /** 963 * Retrieves the {@link MissedCallNotifier} 964 * @return The {@link MissedCallNotifier}. 965 */ getMissedCallNotifier()966 MissedCallNotifier getMissedCallNotifier() { 967 return mMissedCallNotifier; 968 } 969 970 /** 971 * Adds the specified call to the main list of live calls. 972 * 973 * @param call The call to add. 974 */ addCall(Call call)975 private void addCall(Call call) { 976 Log.v(this, "addCall(%s)", call); 977 978 call.addListener(this); 979 mCalls.add(call); 980 981 // TODO: Update mForegroundCall prior to invoking 982 // onCallAdded for calls which immediately take the foreground (like the first call). 983 for (CallsManagerListener listener : mListeners) { 984 listener.onCallAdded(call); 985 } 986 updateForegroundCall(); 987 } 988 removeCall(Call call)989 private void removeCall(Call call) { 990 Log.v(this, "removeCall(%s)", call); 991 992 call.setParentCall(null); // need to clean up parent relationship before destroying. 993 call.removeListener(this); 994 call.clearConnectionService(); 995 996 boolean shouldNotify = false; 997 if (mCalls.contains(call)) { 998 mCalls.remove(call); 999 shouldNotify = true; 1000 } 1001 1002 // Only broadcast changes for calls that are being tracked. 1003 if (shouldNotify) { 1004 for (CallsManagerListener listener : mListeners) { 1005 listener.onCallRemoved(call); 1006 } 1007 updateForegroundCall(); 1008 } 1009 1010 // Now that a call has been removed, other calls may gain new call capabilities (for 1011 // example, if only one call is left, it is now add-call capable again). Trigger the 1012 // recalculation of the call's current capabilities by forcing an update. (See 1013 // InCallController.toParcelableCall()). 1014 for (Call otherCall : mCalls) { 1015 otherCall.setCallCapabilities(otherCall.getCallCapabilities(), true /* forceUpdate */); 1016 } 1017 } 1018 1019 /** 1020 * Sets the specified state on the specified call. 1021 * 1022 * @param call The call. 1023 * @param newState The new state of the call. 1024 */ setCallState(Call call, int newState)1025 private void setCallState(Call call, int newState) { 1026 if (call == null) { 1027 return; 1028 } 1029 int oldState = call.getState(); 1030 Log.i(this, "setCallState %s -> %s, call: %s", CallState.toString(oldState), 1031 CallState.toString(newState), call); 1032 if (newState != oldState) { 1033 // Unfortunately, in the telephony world the radio is king. So if the call notifies 1034 // us that the call is in a particular state, we allow it even if it doesn't make 1035 // sense (e.g., STATE_ACTIVE -> STATE_RINGING). 1036 // TODO: Consider putting a stop to the above and turning CallState 1037 // into a well-defined state machine. 1038 // TODO: Define expected state transitions here, and log when an 1039 // unexpected transition occurs. 1040 call.setState(newState); 1041 1042 // Only broadcast state change for calls that are being tracked. 1043 if (mCalls.contains(call)) { 1044 for (CallsManagerListener listener : mListeners) { 1045 listener.onCallStateChanged(call, oldState, newState); 1046 } 1047 updateForegroundCall(); 1048 } 1049 } 1050 } 1051 1052 /** 1053 * Checks which call should be visible to the user and have audio focus. 1054 */ updateForegroundCall()1055 private void updateForegroundCall() { 1056 Call newForegroundCall = null; 1057 for (Call call : mCalls) { 1058 // TODO: Foreground-ness needs to be explicitly set. No call, regardless 1059 // of its state will be foreground by default and instead the connection service should 1060 // be notified when its calls enter and exit foreground state. Foreground will mean that 1061 // the call should play audio and listen to microphone if it wants. 1062 1063 // Only top-level calls can be in foreground 1064 if (call.getParentCall() != null) { 1065 continue; 1066 } 1067 1068 // Active calls have priority. 1069 if (call.isActive()) { 1070 newForegroundCall = call; 1071 break; 1072 } 1073 1074 if (call.isAlive() || call.getState() == CallState.RINGING) { 1075 newForegroundCall = call; 1076 // Don't break in case there's an active call that has priority. 1077 } 1078 } 1079 1080 if (newForegroundCall != mForegroundCall) { 1081 Log.v(this, "Updating foreground call, %s -> %s.", mForegroundCall, newForegroundCall); 1082 Call oldForegroundCall = mForegroundCall; 1083 mForegroundCall = newForegroundCall; 1084 1085 for (CallsManagerListener listener : mListeners) { 1086 listener.onForegroundCallChanged(oldForegroundCall, mForegroundCall); 1087 } 1088 } 1089 } 1090 isPotentialMMICode(Uri handle)1091 private boolean isPotentialMMICode(Uri handle) { 1092 return (handle != null && handle.getSchemeSpecificPart() != null 1093 && handle.getSchemeSpecificPart().contains("#")); 1094 } 1095 1096 /** 1097 * Determines if a dialed number is potentially an In-Call MMI code. In-Call MMI codes are 1098 * MMI codes which can be dialed when one or more calls are in progress. 1099 * <P> 1100 * Checks for numbers formatted similar to the MMI codes defined in: 1101 * {@link com.android.internal.telephony.gsm.GSMPhone#handleInCallMmiCommands(String)} 1102 * and 1103 * {@link com.android.internal.telephony.imsphone.ImsPhone#handleInCallMmiCommands(String)} 1104 * 1105 * @param handle The URI to call. 1106 * @return {@code True} if the URI represents a number which could be an in-call MMI code. 1107 */ isPotentialInCallMMICode(Uri handle)1108 private boolean isPotentialInCallMMICode(Uri handle) { 1109 if (handle != null && handle.getSchemeSpecificPart() != null && 1110 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) { 1111 1112 String dialedNumber = handle.getSchemeSpecificPart(); 1113 return (dialedNumber.equals("0") || 1114 (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) || 1115 (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) || 1116 dialedNumber.equals("3") || 1117 dialedNumber.equals("4") || 1118 dialedNumber.equals("5")); 1119 } 1120 return false; 1121 } 1122 getNumCallsWithState(int... states)1123 private int getNumCallsWithState(int... states) { 1124 int count = 0; 1125 for (int state : states) { 1126 for (Call call : mCalls) { 1127 if (call.getState() == state) { 1128 count++; 1129 } 1130 } 1131 } 1132 return count; 1133 } 1134 hasMaximumLiveCalls()1135 private boolean hasMaximumLiveCalls() { 1136 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(LIVE_CALL_STATES); 1137 } 1138 hasMaximumHoldingCalls()1139 private boolean hasMaximumHoldingCalls() { 1140 return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(CallState.ON_HOLD); 1141 } 1142 hasMaximumRingingCalls()1143 private boolean hasMaximumRingingCalls() { 1144 return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(CallState.RINGING); 1145 } 1146 hasMaximumOutgoingCalls()1147 private boolean hasMaximumOutgoingCalls() { 1148 return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(OUTGOING_CALL_STATES); 1149 } 1150 makeRoomForOutgoingCall(Call call, boolean isEmergency)1151 private boolean makeRoomForOutgoingCall(Call call, boolean isEmergency) { 1152 if (hasMaximumLiveCalls()) { 1153 // NOTE: If the amount of live calls changes beyond 1, this logic will probably 1154 // have to change. 1155 Call liveCall = getFirstCallWithState(call, LIVE_CALL_STATES); 1156 1157 if (call == liveCall) { 1158 // If the call is already the foreground call, then we are golden. 1159 // This can happen after the user selects an account in the PRE_DIAL_WAIT 1160 // state since the call was already populated into the list. 1161 return true; 1162 } 1163 1164 if (hasMaximumOutgoingCalls()) { 1165 // Disconnect the current outgoing call if it's not an emergency call. If the user 1166 // tries to make two outgoing calls to different emergency call numbers, we will try 1167 // to connect the first outgoing call. 1168 if (isEmergency) { 1169 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES); 1170 if (!outgoingCall.isEmergencyCall()) { 1171 outgoingCall.disconnect(); 1172 return true; 1173 } 1174 } 1175 return false; 1176 } 1177 1178 if (hasMaximumHoldingCalls()) { 1179 // There is no more room for any more calls, unless it's an emergency. 1180 if (isEmergency) { 1181 // Kill the current active call, this is easier then trying to disconnect a 1182 // holding call and hold an active call. 1183 liveCall.disconnect(); 1184 return true; 1185 } 1186 return false; // No more room! 1187 } 1188 1189 // We have room for at least one more holding call at this point. 1190 1191 // First thing, if we are trying to make a call with the same phone account as the live 1192 // call, then allow it so that the connection service can make its own decision about 1193 // how to handle the new call relative to the current one. 1194 if (Objects.equals(liveCall.getTargetPhoneAccount(), call.getTargetPhoneAccount())) { 1195 return true; 1196 } else if (call.getTargetPhoneAccount() == null) { 1197 // Without a phone account, we can't say reliably that the call will fail. 1198 // If the user chooses the same phone account as the live call, then it's 1199 // still possible that the call can be made (like with CDMA calls not supporting 1200 // hold but they still support adding a call by going immediately into conference 1201 // mode). Return true here and we'll run this code again after user chooses an 1202 // account. 1203 return true; 1204 } 1205 1206 // Try to hold the live call before attempting the new outgoing call. 1207 if (liveCall.can(PhoneCapabilities.HOLD)) { 1208 liveCall.hold(); 1209 return true; 1210 } 1211 1212 // The live call cannot be held so we're out of luck here. There's no room. 1213 return false; 1214 } 1215 return true; 1216 } 1217 1218 /** 1219 * Dumps the state of the {@link CallsManager}. 1220 * 1221 * @param pw The {@code IndentingPrintWriter} to write the state to. 1222 */ dump(IndentingPrintWriter pw)1223 public void dump(IndentingPrintWriter pw) { 1224 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 1225 pw.increaseIndent(); 1226 if (mCalls != null) { 1227 pw.println("mCalls: "); 1228 pw.increaseIndent(); 1229 for (Call call : mCalls) { 1230 pw.println(call); 1231 } 1232 pw.decreaseIndent(); 1233 } 1234 pw.decreaseIndent(); 1235 } 1236 } 1237