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