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