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 static android.telecom.TelecomManager.ACTION_POST_CALL; 20 import static android.telecom.TelecomManager.DURATION_LONG; 21 import static android.telecom.TelecomManager.DURATION_MEDIUM; 22 import static android.telecom.TelecomManager.DURATION_SHORT; 23 import static android.telecom.TelecomManager.DURATION_VERY_SHORT; 24 import static android.telecom.TelecomManager.EXTRA_CALL_DURATION; 25 import static android.telecom.TelecomManager.EXTRA_DISCONNECT_CAUSE; 26 import static android.telecom.TelecomManager.EXTRA_HANDLE; 27 import static android.telecom.TelecomManager.MEDIUM_CALL_TIME_MS; 28 import static android.telecom.TelecomManager.SHORT_CALL_TIME_MS; 29 import static android.telecom.TelecomManager.VERY_SHORT_CALL_TIME_MS; 30 31 import android.Manifest; 32 import android.annotation.NonNull; 33 import android.app.ActivityManager; 34 import android.app.AlertDialog; 35 import android.app.KeyguardManager; 36 import android.content.BroadcastReceiver; 37 import android.content.ComponentName; 38 import android.content.Context; 39 import android.content.DialogInterface; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.pm.PackageManager; 43 import android.content.pm.UserInfo; 44 import android.graphics.Color; 45 import android.graphics.drawable.ColorDrawable; 46 import android.media.AudioManager; 47 import android.media.AudioSystem; 48 import android.media.MediaPlayer; 49 import android.media.ToneGenerator; 50 import android.net.Uri; 51 import android.os.AsyncTask; 52 import android.os.Bundle; 53 import android.os.Handler; 54 import android.os.HandlerThread; 55 import android.os.Looper; 56 import android.os.PersistableBundle; 57 import android.os.Process; 58 import android.os.SystemClock; 59 import android.os.SystemVibrator; 60 import android.os.Trace; 61 import android.os.UserHandle; 62 import android.os.UserManager; 63 import android.provider.BlockedNumberContract; 64 import android.provider.BlockedNumberContract.SystemContract; 65 import android.provider.CallLog.Calls; 66 import android.provider.Settings; 67 import android.sysprop.TelephonyProperties; 68 import android.telecom.CallAudioState; 69 import android.telecom.CallerInfo; 70 import android.telecom.Conference; 71 import android.telecom.Connection; 72 import android.telecom.DisconnectCause; 73 import android.telecom.GatewayInfo; 74 import android.telecom.Log; 75 import android.telecom.Logging.Runnable; 76 import android.telecom.Logging.Session; 77 import android.telecom.ParcelableConference; 78 import android.telecom.ParcelableConnection; 79 import android.telecom.PhoneAccount; 80 import android.telecom.PhoneAccountHandle; 81 import android.telecom.PhoneAccountSuggestion; 82 import android.telecom.TelecomManager; 83 import android.telecom.VideoProfile; 84 import android.telephony.CarrierConfigManager; 85 import android.telephony.PhoneNumberUtils; 86 import android.telephony.TelephonyManager; 87 import android.text.TextUtils; 88 import android.util.Pair; 89 import android.view.LayoutInflater; 90 import android.view.View; 91 import android.view.WindowManager; 92 import android.widget.Button; 93 94 import com.android.internal.annotations.VisibleForTesting; 95 import com.android.internal.util.IndentingPrintWriter; 96 import com.android.server.telecom.bluetooth.BluetoothRouteManager; 97 import com.android.server.telecom.bluetooth.BluetoothStateReceiver; 98 import com.android.server.telecom.callfiltering.BlockCheckerAdapter; 99 import com.android.server.telecom.callfiltering.BlockCheckerFilter; 100 import com.android.server.telecom.callfiltering.CallFilterResultCallback; 101 import com.android.server.telecom.callfiltering.CallFilteringResult; 102 import com.android.server.telecom.callfiltering.CallFilteringResult.Builder; 103 import com.android.server.telecom.callfiltering.CallScreeningServiceFilter; 104 import com.android.server.telecom.callfiltering.DirectToVoicemailFilter; 105 import com.android.server.telecom.callfiltering.IncomingCallFilter; 106 import com.android.server.telecom.callfiltering.IncomingCallFilterGraph; 107 import com.android.server.telecom.callredirection.CallRedirectionProcessor; 108 import com.android.server.telecom.components.ErrorDialogActivity; 109 import com.android.server.telecom.components.TelecomBroadcastReceiver; 110 import com.android.server.telecom.settings.BlockedNumbersUtil; 111 import com.android.server.telecom.ui.AudioProcessingNotification; 112 import com.android.server.telecom.ui.CallRedirectionTimeoutDialogActivity; 113 import com.android.server.telecom.ui.ConfirmCallDialogActivity; 114 import com.android.server.telecom.ui.DisconnectedCallNotifier; 115 import com.android.server.telecom.ui.IncomingCallNotifier; 116 import com.android.server.telecom.ui.ToastFactory; 117 118 import java.util.ArrayList; 119 import java.util.Arrays; 120 import java.util.Collection; 121 import java.util.Collections; 122 import java.util.HashMap; 123 import java.util.HashSet; 124 import java.util.Iterator; 125 import java.util.LinkedList; 126 import java.util.List; 127 import java.util.Map; 128 import java.util.Objects; 129 import java.util.Optional; 130 import java.util.Set; 131 import java.util.concurrent.CompletableFuture; 132 import java.util.concurrent.ConcurrentHashMap; 133 import java.util.concurrent.CountDownLatch; 134 import java.util.concurrent.Executors; 135 import java.util.concurrent.TimeUnit; 136 import java.util.stream.Collectors; 137 import java.util.stream.IntStream; 138 import java.util.stream.Stream; 139 140 /** 141 * Singleton. 142 * 143 * NOTE: by design most APIs are package private, use the relevant adapter/s to allow 144 * access from other packages specifically refraining from passing the CallsManager instance 145 * beyond the com.android.server.telecom package boundary. 146 */ 147 @VisibleForTesting 148 public class CallsManager extends Call.ListenerBase 149 implements VideoProviderProxy.Listener, CallFilterResultCallback, CurrentUserProxy { 150 151 // TODO: Consider renaming this CallsManagerPlugin. 152 @VisibleForTesting 153 public interface CallsManagerListener { onCallAdded(Call call)154 void onCallAdded(Call call); onCallRemoved(Call call)155 void onCallRemoved(Call call); onCallStateChanged(Call call, int oldState, int newState)156 void onCallStateChanged(Call call, int oldState, int newState); onConnectionServiceChanged( Call call, ConnectionServiceWrapper oldService, ConnectionServiceWrapper newService)157 void onConnectionServiceChanged( 158 Call call, 159 ConnectionServiceWrapper oldService, 160 ConnectionServiceWrapper newService); onIncomingCallAnswered(Call call)161 void onIncomingCallAnswered(Call call); onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage)162 void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage); onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState)163 void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState newAudioState); onRingbackRequested(Call call, boolean ringback)164 void onRingbackRequested(Call call, boolean ringback); onIsConferencedChanged(Call call)165 void onIsConferencedChanged(Call call); onIsVoipAudioModeChanged(Call call)166 void onIsVoipAudioModeChanged(Call call); onVideoStateChanged(Call call, int previousVideoState, int newVideoState)167 void onVideoStateChanged(Call call, int previousVideoState, int newVideoState); onCanAddCallChanged(boolean canAddCall)168 void onCanAddCallChanged(boolean canAddCall); onSessionModifyRequestReceived(Call call, VideoProfile videoProfile)169 void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile); onHoldToneRequested(Call call)170 void onHoldToneRequested(Call call); onExternalCallChanged(Call call, boolean isExternalCall)171 void onExternalCallChanged(Call call, boolean isExternalCall); onDisconnectedTonePlaying(boolean isTonePlaying)172 void onDisconnectedTonePlaying(boolean isTonePlaying); onConnectionTimeChanged(Call call)173 void onConnectionTimeChanged(Call call); onConferenceStateChanged(Call call, boolean isConference)174 void onConferenceStateChanged(Call call, boolean isConference); onCdmaConferenceSwap(Call call)175 void onCdmaConferenceSwap(Call call); 176 } 177 178 /** Interface used to define the action which is executed delay under some condition. */ 179 interface PendingAction { performAction()180 void performAction(); 181 } 182 183 private static final String TAG = "CallsManager"; 184 185 /** 186 * Call filter specifier used with 187 * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate only 188 * self-managed calls should be included. 189 */ 190 private static final int CALL_FILTER_SELF_MANAGED = 1; 191 192 /** 193 * Call filter specifier used with 194 * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate only 195 * managed calls should be included. 196 */ 197 private static final int CALL_FILTER_MANAGED = 2; 198 199 /** 200 * Call filter specifier used with 201 * {@link #getNumCallsWithState(int, Call, PhoneAccountHandle, int...)} to indicate both managed 202 * and self-managed calls should be included. 203 */ 204 private static final int CALL_FILTER_ALL = 3; 205 206 private static final String PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION = 207 "android.permission.PROCESS_PHONE_ACCOUNT_REGISTRATION"; 208 209 private static final int HANDLER_WAIT_TIMEOUT = 10000; 210 private static final int MAXIMUM_LIVE_CALLS = 1; 211 private static final int MAXIMUM_HOLD_CALLS = 1; 212 private static final int MAXIMUM_RINGING_CALLS = 1; 213 private static final int MAXIMUM_DIALING_CALLS = 1; 214 private static final int MAXIMUM_OUTGOING_CALLS = 1; 215 private static final int MAXIMUM_TOP_LEVEL_CALLS = 2; 216 private static final int MAXIMUM_SELF_MANAGED_CALLS = 10; 217 218 private static final int[] OUTGOING_CALL_STATES = 219 {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, 220 CallState.PULLING}; 221 222 /** 223 * These states are used by {@link #makeRoomForOutgoingCall(Call, boolean)} to determine which 224 * call should be ended first to make room for a new outgoing call. 225 */ 226 private static final int[] LIVE_CALL_STATES = 227 {CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, 228 CallState.PULLING, CallState.ACTIVE, CallState.AUDIO_PROCESSING}; 229 230 /** 231 * These states determine which calls will cause {@link TelecomManager#isInCall()} or 232 * {@link TelecomManager#isInManagedCall()} to return true. 233 * 234 * See also {@link PhoneStateBroadcaster}, which considers a similar set of states as being 235 * off-hook. 236 */ 237 public static final int[] ONGOING_CALL_STATES = 238 {CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, CallState.PULLING, CallState.ACTIVE, 239 CallState.ON_HOLD, CallState.RINGING, CallState.SIMULATED_RINGING, 240 CallState.ANSWERED, CallState.AUDIO_PROCESSING}; 241 242 private static final int[] ANY_CALL_STATE = 243 {CallState.NEW, CallState.CONNECTING, CallState.SELECT_PHONE_ACCOUNT, CallState.DIALING, 244 CallState.RINGING, CallState.SIMULATED_RINGING, CallState.ACTIVE, 245 CallState.ON_HOLD, CallState.DISCONNECTED, CallState.ABORTED, 246 CallState.DISCONNECTING, CallState.PULLING, CallState.ANSWERED, 247 CallState.AUDIO_PROCESSING}; 248 249 public static final String TELECOM_CALL_ID_PREFIX = "TC@"; 250 251 // Maps call technologies in TelephonyManager to those in Analytics. 252 private static final Map<Integer, Integer> sAnalyticsTechnologyMap; 253 static { 254 sAnalyticsTechnologyMap = new HashMap<>(5); sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE)255 sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_CDMA, Analytics.CDMA_PHONE); sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_GSM, Analytics.GSM_PHONE)256 sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_GSM, Analytics.GSM_PHONE); sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_IMS, Analytics.IMS_PHONE)257 sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_IMS, Analytics.IMS_PHONE); sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_SIP, Analytics.SIP_PHONE)258 sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_SIP, Analytics.SIP_PHONE); sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_THIRD_PARTY, Analytics.THIRD_PARTY_PHONE)259 sAnalyticsTechnologyMap.put(TelephonyManager.PHONE_TYPE_THIRD_PARTY, 260 Analytics.THIRD_PARTY_PHONE); 261 } 262 263 /** 264 * The main call repository. Keeps an instance of all live calls. New incoming and outgoing 265 * calls are added to the map and removed when the calls move to the disconnected state. 266 * 267 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is 268 * load factor before resizing, 1 means we only expect a single thread to 269 * access the map so make only a single shard 270 */ 271 private final Set<Call> mCalls = Collections.newSetFromMap( 272 new ConcurrentHashMap<Call, Boolean>(8, 0.9f, 1)); 273 274 /** 275 * A pending call is one which requires user-intervention in order to be placed. 276 * Used by {@link #startCallConfirmation}. 277 */ 278 private Call mPendingCall; 279 /** 280 * Cached latest pending redirected call which requires user-intervention in order to be placed. 281 * Used by {@link #onCallRedirectionComplete}. 282 */ 283 private Call mPendingRedirectedOutgoingCall; 284 285 /** 286 * Cached call that's been answered but will be added to mCalls pending confirmation of active 287 * status from the connection service. 288 */ 289 private Call mPendingAudioProcessingCall; 290 291 /** 292 * Cached latest pending redirected call information which require user-intervention in order 293 * to be placed. Used by {@link #onCallRedirectionComplete}. 294 */ 295 private final Map<String, Runnable> mPendingRedirectedOutgoingCallInfo = 296 new ConcurrentHashMap<>(); 297 /** 298 * Cached latest pending Unredirected call information which require user-intervention in order 299 * to be placed. Used by {@link #onCallRedirectionComplete}. 300 */ 301 private final Map<String, Runnable> mPendingUnredirectedOutgoingCallInfo = 302 new ConcurrentHashMap<>(); 303 304 private CompletableFuture<Call> mPendingCallConfirm; 305 private CompletableFuture<Pair<Call, PhoneAccountHandle>> mPendingAccountSelection; 306 307 // Instance variables for testing -- we keep the latest copy of the outgoing call futures 308 // here so that we can wait on them in tests 309 private CompletableFuture<Call> mLatestPostSelectionProcessingFuture; 310 private CompletableFuture<Pair<Call, List<PhoneAccountSuggestion>>> 311 mLatestPreAccountSelectionFuture; 312 313 /** 314 * The current telecom call ID. Used when creating new instances of {@link Call}. Should 315 * only be accessed using the {@link #getNextCallId()} method which synchronizes on the 316 * {@link #mLock} sync root. 317 */ 318 private int mCallId = 0; 319 320 private int mRttRequestId = 0; 321 /** 322 * Stores the current foreground user. 323 */ 324 private UserHandle mCurrentUserHandle = UserHandle.of(ActivityManager.getCurrentUser()); 325 326 private final ConnectionServiceRepository mConnectionServiceRepository; 327 private final DtmfLocalTonePlayer mDtmfLocalTonePlayer; 328 private final InCallController mInCallController; 329 private final CallAudioManager mCallAudioManager; 330 private final CallRecordingTonePlayer mCallRecordingTonePlayer; 331 private RespondViaSmsManager mRespondViaSmsManager; 332 private final Ringer mRinger; 333 private final InCallWakeLockController mInCallWakeLockController; 334 // For this set initial table size to 16 because we add 13 listeners in 335 // the CallsManager constructor. 336 private final Set<CallsManagerListener> mListeners = Collections.newSetFromMap( 337 new ConcurrentHashMap<CallsManagerListener, Boolean>(16, 0.9f, 1)); 338 private final HeadsetMediaButton mHeadsetMediaButton; 339 private final WiredHeadsetManager mWiredHeadsetManager; 340 private final SystemStateHelper mSystemStateHelper; 341 private final BluetoothRouteManager mBluetoothRouteManager; 342 private final DockManager mDockManager; 343 private final TtyManager mTtyManager; 344 private final ProximitySensorManager mProximitySensorManager; 345 private final PhoneStateBroadcaster mPhoneStateBroadcaster; 346 private final CallLogManager mCallLogManager; 347 private final Context mContext; 348 private final TelecomSystem.SyncRoot mLock; 349 private final PhoneAccountRegistrar mPhoneAccountRegistrar; 350 private final MissedCallNotifier mMissedCallNotifier; 351 private final DisconnectedCallNotifier mDisconnectedCallNotifier; 352 private IncomingCallNotifier mIncomingCallNotifier; 353 private final CallerInfoLookupHelper mCallerInfoLookupHelper; 354 private final IncomingCallFilter.Factory mIncomingCallFilterFactory; 355 private final DefaultDialerCache mDefaultDialerCache; 356 private final Timeouts.Adapter mTimeoutsAdapter; 357 private final PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter; 358 private final ClockProxy mClockProxy; 359 private final ToastFactory mToastFactory; 360 private final Set<Call> mLocallyDisconnectingCalls = new HashSet<>(); 361 private final Set<Call> mPendingCallsToDisconnect = new HashSet<>(); 362 private final ConnectionServiceFocusManager mConnectionSvrFocusMgr; 363 /* Handler tied to thread in which CallManager was initialized. */ 364 private final Handler mHandler = new Handler(Looper.getMainLooper()); 365 private final EmergencyCallHelper mEmergencyCallHelper; 366 private final RoleManagerAdapter mRoleManagerAdapter; 367 368 private final ConnectionServiceFocusManager.CallsManagerRequester mRequester = 369 new ConnectionServiceFocusManager.CallsManagerRequester() { 370 @Override 371 public void releaseConnectionService( 372 ConnectionServiceFocusManager.ConnectionServiceFocus connectionService) { 373 mCalls.stream() 374 .filter(c -> c.getConnectionServiceWrapper().equals(connectionService)) 375 .forEach(c -> c.disconnect("release " + 376 connectionService.getComponentName().getPackageName())); 377 } 378 379 @Override 380 public void setCallsManagerListener(CallsManagerListener listener) { 381 mListeners.add(listener); 382 } 383 }; 384 385 private boolean mCanAddCall = true; 386 387 private int mMaxNumberOfSimultaneouslyActiveSims = -1; 388 389 private Runnable mStopTone; 390 391 private LinkedList<HandlerThread> mGraphHandlerThreads; 392 393 private boolean mHasActiveRttCall = false; 394 395 /** 396 * Listener to PhoneAccountRegistrar events. 397 */ 398 private PhoneAccountRegistrar.Listener mPhoneAccountListener = 399 new PhoneAccountRegistrar.Listener() { 400 public void onPhoneAccountRegistered(PhoneAccountRegistrar registrar, 401 PhoneAccountHandle handle) { 402 broadcastRegisterIntent(handle); 403 } 404 public void onPhoneAccountUnRegistered(PhoneAccountRegistrar registrar, 405 PhoneAccountHandle handle) { 406 broadcastUnregisterIntent(handle); 407 } 408 409 @Override 410 public void onPhoneAccountChanged(PhoneAccountRegistrar registrar, 411 PhoneAccount phoneAccount) { 412 handlePhoneAccountChanged(registrar, phoneAccount); 413 } 414 }; 415 416 /** 417 * Receiver for enhanced call blocking feature to update the emergency call notification 418 * in below cases: 419 * 1) Carrier config changed. 420 * 2) Blocking suppression state changed. 421 */ 422 private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 423 @Override 424 public void onReceive(Context context, Intent intent) { 425 Log.startSession("CM.CCCR"); 426 String action = intent.getAction(); 427 if (CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED.equals(action) 428 || SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED.equals(action)) { 429 new UpdateEmergencyCallNotificationTask().doInBackground( 430 Pair.create(context, Log.createSubsession())); 431 } 432 } 433 }; 434 435 private static class UpdateEmergencyCallNotificationTask 436 extends AsyncTask<Pair<Context, Session>, Void, Void> { 437 @SafeVarargs 438 @Override doInBackground(Pair<Context, Session>.... args)439 protected final Void doInBackground(Pair<Context, Session>... args) { 440 if (args == null || args.length != 1 || args[0] == null) { 441 Log.e(this, new IllegalArgumentException(), "Incorrect invocation"); 442 return null; 443 } 444 Log.continueSession(args[0].second, "CM.UECNT"); 445 Context context = args[0].first; 446 BlockedNumbersUtil.updateEmergencyCallNotification(context, 447 SystemContract.shouldShowEmergencyCallNotification(context)); 448 Log.endSession(); 449 return null; 450 } 451 } 452 453 /** 454 * Initializes the required Telecom components. 455 */ 456 @VisibleForTesting CallsManager( Context context, TelecomSystem.SyncRoot lock, CallerInfoLookupHelper callerInfoLookupHelper, MissedCallNotifier missedCallNotifier, DisconnectedCallNotifier.Factory disconnectedCallNotifierFactory, PhoneAccountRegistrar phoneAccountRegistrar, HeadsetMediaButtonFactory headsetMediaButtonFactory, ProximitySensorManagerFactory proximitySensorManagerFactory, InCallWakeLockControllerFactory inCallWakeLockControllerFactory, ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory connectionServiceFocusManagerFactory, CallAudioManager.AudioServiceFactory audioServiceFactory, BluetoothRouteManager bluetoothManager, WiredHeadsetManager wiredHeadsetManager, SystemStateHelper systemStateHelper, DefaultDialerCache defaultDialerCache, Timeouts.Adapter timeoutsAdapter, AsyncRingtonePlayer asyncRingtonePlayer, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, EmergencyCallHelper emergencyCallHelper, InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory, ClockProxy clockProxy, AudioProcessingNotification audioProcessingNotification, BluetoothStateReceiver bluetoothStateReceiver, CallAudioRouteStateMachine.Factory callAudioRouteStateMachineFactory, CallAudioModeStateMachine.Factory callAudioModeStateMachineFactory, InCallControllerFactory inCallControllerFactory, RoleManagerAdapter roleManagerAdapter, IncomingCallFilter.Factory incomingCallFilterFactory, ToastFactory toastFactory)457 public CallsManager( 458 Context context, 459 TelecomSystem.SyncRoot lock, 460 CallerInfoLookupHelper callerInfoLookupHelper, 461 MissedCallNotifier missedCallNotifier, 462 DisconnectedCallNotifier.Factory disconnectedCallNotifierFactory, 463 PhoneAccountRegistrar phoneAccountRegistrar, 464 HeadsetMediaButtonFactory headsetMediaButtonFactory, 465 ProximitySensorManagerFactory proximitySensorManagerFactory, 466 InCallWakeLockControllerFactory inCallWakeLockControllerFactory, 467 ConnectionServiceFocusManager.ConnectionServiceFocusManagerFactory 468 connectionServiceFocusManagerFactory, 469 CallAudioManager.AudioServiceFactory audioServiceFactory, 470 BluetoothRouteManager bluetoothManager, 471 WiredHeadsetManager wiredHeadsetManager, 472 SystemStateHelper systemStateHelper, 473 DefaultDialerCache defaultDialerCache, 474 Timeouts.Adapter timeoutsAdapter, 475 AsyncRingtonePlayer asyncRingtonePlayer, 476 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, 477 EmergencyCallHelper emergencyCallHelper, 478 InCallTonePlayer.ToneGeneratorFactory toneGeneratorFactory, 479 ClockProxy clockProxy, 480 AudioProcessingNotification audioProcessingNotification, 481 BluetoothStateReceiver bluetoothStateReceiver, 482 CallAudioRouteStateMachine.Factory callAudioRouteStateMachineFactory, 483 CallAudioModeStateMachine.Factory callAudioModeStateMachineFactory, 484 InCallControllerFactory inCallControllerFactory, 485 RoleManagerAdapter roleManagerAdapter, 486 IncomingCallFilter.Factory incomingCallFilterFactory, 487 ToastFactory toastFactory) { 488 mContext = context; 489 mLock = lock; 490 mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter; 491 mPhoneAccountRegistrar = phoneAccountRegistrar; 492 mPhoneAccountRegistrar.addListener(mPhoneAccountListener); 493 mMissedCallNotifier = missedCallNotifier; 494 mDisconnectedCallNotifier = disconnectedCallNotifierFactory.create(mContext, this); 495 StatusBarNotifier statusBarNotifier = new StatusBarNotifier(context, this); 496 mWiredHeadsetManager = wiredHeadsetManager; 497 mSystemStateHelper = systemStateHelper; 498 mDefaultDialerCache = defaultDialerCache; 499 mBluetoothRouteManager = bluetoothManager; 500 mDockManager = new DockManager(context); 501 mTimeoutsAdapter = timeoutsAdapter; 502 mEmergencyCallHelper = emergencyCallHelper; 503 mCallerInfoLookupHelper = callerInfoLookupHelper; 504 mIncomingCallFilterFactory = incomingCallFilterFactory; 505 506 mDtmfLocalTonePlayer = 507 new DtmfLocalTonePlayer(new DtmfLocalTonePlayer.ToneGeneratorProxy()); 508 CallAudioRouteStateMachine callAudioRouteStateMachine = 509 callAudioRouteStateMachineFactory.create( 510 context, 511 this, 512 bluetoothManager, 513 wiredHeadsetManager, 514 statusBarNotifier, 515 audioServiceFactory, 516 CallAudioRouteStateMachine.EARPIECE_AUTO_DETECT 517 ); 518 callAudioRouteStateMachine.initialize(); 519 520 CallAudioRoutePeripheralAdapter callAudioRoutePeripheralAdapter = 521 new CallAudioRoutePeripheralAdapter( 522 callAudioRouteStateMachine, 523 bluetoothManager, 524 wiredHeadsetManager, 525 mDockManager); 526 527 AudioManager audioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 528 InCallTonePlayer.MediaPlayerFactory mediaPlayerFactory = 529 (resourceId, attributes) -> 530 new InCallTonePlayer.MediaPlayerAdapterImpl( 531 MediaPlayer.create(mContext, resourceId, attributes, 532 audioManager.generateAudioSessionId())); 533 InCallTonePlayer.Factory playerFactory = new InCallTonePlayer.Factory( 534 callAudioRoutePeripheralAdapter, lock, toneGeneratorFactory, mediaPlayerFactory, 535 () -> audioManager.getStreamVolume(AudioManager.STREAM_RING) > 0); 536 537 SystemSettingsUtil systemSettingsUtil = new SystemSettingsUtil(); 538 RingtoneFactory ringtoneFactory = new RingtoneFactory(this, context); 539 SystemVibrator systemVibrator = new SystemVibrator(context); 540 mInCallController = inCallControllerFactory.create(context, mLock, this, 541 systemStateHelper, defaultDialerCache, mTimeoutsAdapter, 542 emergencyCallHelper); 543 mRinger = new Ringer(playerFactory, context, systemSettingsUtil, asyncRingtonePlayer, 544 ringtoneFactory, systemVibrator, 545 new Ringer.VibrationEffectProxy(), mInCallController); 546 mCallRecordingTonePlayer = new CallRecordingTonePlayer(mContext, audioManager, 547 mTimeoutsAdapter, mLock); 548 mCallAudioManager = new CallAudioManager(callAudioRouteStateMachine, 549 this, callAudioModeStateMachineFactory.create(systemStateHelper, 550 (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE)), 551 playerFactory, mRinger, new RingbackPlayer(playerFactory), 552 bluetoothStateReceiver, mDtmfLocalTonePlayer); 553 554 mConnectionSvrFocusMgr = connectionServiceFocusManagerFactory.create(mRequester); 555 mHeadsetMediaButton = headsetMediaButtonFactory.create(context, this, mLock); 556 mTtyManager = new TtyManager(context, mWiredHeadsetManager); 557 mProximitySensorManager = proximitySensorManagerFactory.create(context, this); 558 mPhoneStateBroadcaster = new PhoneStateBroadcaster(this); 559 mCallLogManager = new CallLogManager(context, phoneAccountRegistrar, mMissedCallNotifier); 560 mConnectionServiceRepository = 561 new ConnectionServiceRepository(mPhoneAccountRegistrar, mContext, mLock, this); 562 mInCallWakeLockController = inCallWakeLockControllerFactory.create(context, this); 563 mClockProxy = clockProxy; 564 mToastFactory = toastFactory; 565 mRoleManagerAdapter = roleManagerAdapter; 566 567 mListeners.add(mInCallWakeLockController); 568 mListeners.add(statusBarNotifier); 569 mListeners.add(mCallLogManager); 570 mListeners.add(mPhoneStateBroadcaster); 571 mListeners.add(mInCallController); 572 mListeners.add(mCallAudioManager); 573 mListeners.add(mCallRecordingTonePlayer); 574 mListeners.add(missedCallNotifier); 575 mListeners.add(mDisconnectedCallNotifier); 576 mListeners.add(mHeadsetMediaButton); 577 mListeners.add(mProximitySensorManager); 578 mListeners.add(audioProcessingNotification); 579 580 // There is no USER_SWITCHED broadcast for user 0, handle it here explicitly. 581 final UserManager userManager = UserManager.get(mContext); 582 // Don't load missed call if it is run in split user model. 583 if (userManager.isPrimaryUser()) { 584 onUserSwitch(Process.myUserHandle()); 585 } 586 // Register BroadcastReceiver to handle enhanced call blocking feature related event. 587 IntentFilter intentFilter = new IntentFilter( 588 CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED); 589 intentFilter.addAction(SystemContract.ACTION_BLOCK_SUPPRESSION_STATE_CHANGED); 590 context.registerReceiver(mReceiver, intentFilter); 591 mGraphHandlerThreads = new LinkedList<>(); 592 } 593 setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier)594 public void setIncomingCallNotifier(IncomingCallNotifier incomingCallNotifier) { 595 if (mIncomingCallNotifier != null) { 596 mListeners.remove(mIncomingCallNotifier); 597 } 598 mIncomingCallNotifier = incomingCallNotifier; 599 mListeners.add(mIncomingCallNotifier); 600 } 601 setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager)602 public void setRespondViaSmsManager(RespondViaSmsManager respondViaSmsManager) { 603 if (mRespondViaSmsManager != null) { 604 mListeners.remove(mRespondViaSmsManager); 605 } 606 mRespondViaSmsManager = respondViaSmsManager; 607 mListeners.add(respondViaSmsManager); 608 } 609 getRespondViaSmsManager()610 public RespondViaSmsManager getRespondViaSmsManager() { 611 return mRespondViaSmsManager; 612 } 613 getCallerInfoLookupHelper()614 public CallerInfoLookupHelper getCallerInfoLookupHelper() { 615 return mCallerInfoLookupHelper; 616 } 617 getRoleManagerAdapter()618 public RoleManagerAdapter getRoleManagerAdapter() { 619 return mRoleManagerAdapter; 620 } 621 622 @Override onSuccessfulOutgoingCall(Call call, int callState)623 public void onSuccessfulOutgoingCall(Call call, int callState) { 624 Log.v(this, "onSuccessfulOutgoingCall, %s", call); 625 call.setPostCallPackageName(getRoleManagerAdapter().getDefaultCallScreeningApp()); 626 627 setCallState(call, callState, "successful outgoing call"); 628 if (!mCalls.contains(call)) { 629 // Call was not added previously in startOutgoingCall due to it being a potential MMI 630 // code, so add it now. 631 addCall(call); 632 } 633 634 // The call's ConnectionService has been updated. 635 for (CallsManagerListener listener : mListeners) { 636 listener.onConnectionServiceChanged(call, null, call.getConnectionService()); 637 } 638 639 markCallAsDialing(call); 640 } 641 642 @Override onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)643 public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) { 644 Log.v(this, "onFailedOutgoingCall, call: %s", call); 645 646 markCallAsRemoved(call); 647 } 648 649 @Override onSuccessfulIncomingCall(Call incomingCall)650 public void onSuccessfulIncomingCall(Call incomingCall) { 651 Log.d(this, "onSuccessfulIncomingCall"); 652 PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked( 653 incomingCall.getTargetPhoneAccount()); 654 Bundle extras = 655 phoneAccount == null || phoneAccount.getExtras() == null 656 ? new Bundle() 657 : phoneAccount.getExtras(); 658 if (incomingCall.hasProperty(Connection.PROPERTY_EMERGENCY_CALLBACK_MODE) || 659 incomingCall.isSelfManaged() || 660 extras.getBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING)) { 661 Log.i(this, "Skipping call filtering for %s (ecm=%b, selfMgd=%b, skipExtra=%b)", 662 incomingCall.getId(), 663 incomingCall.hasProperty(Connection.PROPERTY_EMERGENCY_CALLBACK_MODE), 664 incomingCall.isSelfManaged(), 665 extras.getBoolean(PhoneAccount.EXTRA_SKIP_CALL_FILTERING)); 666 onCallFilteringComplete(incomingCall, new Builder() 667 .setShouldAllowCall(true) 668 .setShouldReject(false) 669 .setShouldAddToCallLog(true) 670 .setShouldShowNotification(true) 671 .build()); 672 incomingCall.setIsUsingCallFiltering(false); 673 return; 674 } 675 676 IncomingCallFilterGraph graph = setUpCallFilterGraph(incomingCall); 677 graph.performFiltering(); 678 } 679 setUpCallFilterGraph(Call incomingCall)680 private IncomingCallFilterGraph setUpCallFilterGraph(Call incomingCall) { 681 incomingCall.setIsUsingCallFiltering(true); 682 String carrierPackageName = getCarrierPackageName(); 683 String defaultDialerPackageName = TelecomManager.from(mContext).getDefaultDialerPackage(); 684 String userChosenPackageName = getRoleManagerAdapter().getDefaultCallScreeningApp(); 685 AppLabelProxy appLabelProxy = packageName -> AppLabelProxy.Util.getAppLabel( 686 mContext.getPackageManager(), packageName); 687 ParcelableCallUtils.Converter converter = new ParcelableCallUtils.Converter(); 688 689 IncomingCallFilterGraph graph = new IncomingCallFilterGraph(incomingCall, 690 this::onCallFilteringComplete, mContext, mTimeoutsAdapter, mLock); 691 DirectToVoicemailFilter voicemailFilter = new DirectToVoicemailFilter(incomingCall, 692 mCallerInfoLookupHelper); 693 BlockCheckerFilter blockCheckerFilter = new BlockCheckerFilter(mContext, incomingCall, 694 mCallerInfoLookupHelper, new BlockCheckerAdapter()); 695 CallScreeningServiceFilter carrierCallScreeningServiceFilter = 696 new CallScreeningServiceFilter(incomingCall, carrierPackageName, 697 CallScreeningServiceFilter.PACKAGE_TYPE_CARRIER, mContext, this, 698 appLabelProxy, converter); 699 CallScreeningServiceFilter callScreeningServiceFilter; 700 if ((userChosenPackageName != null) 701 && (!userChosenPackageName.equals(defaultDialerPackageName))) { 702 callScreeningServiceFilter = new CallScreeningServiceFilter(incomingCall, 703 userChosenPackageName, CallScreeningServiceFilter.PACKAGE_TYPE_USER_CHOSEN, 704 mContext, this, appLabelProxy, converter); 705 } else { 706 callScreeningServiceFilter = new CallScreeningServiceFilter(incomingCall, 707 defaultDialerPackageName, 708 CallScreeningServiceFilter.PACKAGE_TYPE_DEFAULT_DIALER, 709 mContext, this, appLabelProxy, converter); 710 } 711 graph.addFilter(voicemailFilter); 712 graph.addFilter(blockCheckerFilter); 713 graph.addFilter(carrierCallScreeningServiceFilter); 714 graph.addFilter(callScreeningServiceFilter); 715 IncomingCallFilterGraph.addEdge(voicemailFilter, carrierCallScreeningServiceFilter); 716 IncomingCallFilterGraph.addEdge(blockCheckerFilter, carrierCallScreeningServiceFilter); 717 IncomingCallFilterGraph.addEdge(carrierCallScreeningServiceFilter, 718 callScreeningServiceFilter); 719 mGraphHandlerThreads.add(graph.getHandlerThread()); 720 return graph; 721 } 722 getCarrierPackageName()723 private String getCarrierPackageName() { 724 ComponentName componentName = null; 725 CarrierConfigManager configManager = (CarrierConfigManager) mContext.getSystemService 726 (Context.CARRIER_CONFIG_SERVICE); 727 PersistableBundle configBundle = configManager.getConfig(); 728 if (configBundle != null) { 729 componentName = ComponentName.unflattenFromString(configBundle.getString 730 (CarrierConfigManager.KEY_CARRIER_CALL_SCREENING_APP_STRING, "")); 731 } 732 733 return componentName != null ? componentName.getPackageName() : null; 734 } 735 736 @Override onCallFilteringComplete(Call incomingCall, CallFilteringResult result)737 public void onCallFilteringComplete(Call incomingCall, CallFilteringResult result) { 738 // Only set the incoming call as ringing if it isn't already disconnected. It is possible 739 // that the connection service disconnected the call before it was even added to Telecom, in 740 // which case it makes no sense to set it back to a ringing state. 741 mGraphHandlerThreads.clear(); 742 743 if (incomingCall.getState() != CallState.DISCONNECTED && 744 incomingCall.getState() != CallState.DISCONNECTING) { 745 setCallState(incomingCall, CallState.RINGING, 746 result.shouldAllowCall ? "successful incoming call" : "blocking call"); 747 } else { 748 Log.i(this, "onCallFilteringCompleted: call already disconnected."); 749 return; 750 } 751 752 if (result.shouldAllowCall) { 753 incomingCall.setPostCallPackageName( 754 getRoleManagerAdapter().getDefaultCallScreeningApp()); 755 756 if (hasMaximumManagedRingingCalls(incomingCall)) { 757 if (shouldSilenceInsteadOfReject(incomingCall)) { 758 incomingCall.silence(); 759 } else { 760 Log.i(this, "onCallFilteringCompleted: Call rejected! " + 761 "Exceeds maximum number of ringing calls."); 762 rejectCallAndLog(incomingCall, result); 763 } 764 } else if (hasMaximumManagedDialingCalls(incomingCall)) { 765 if (shouldSilenceInsteadOfReject(incomingCall)) { 766 incomingCall.silence(); 767 } else { 768 769 Log.i(this, "onCallFilteringCompleted: Call rejected! Exceeds maximum number of " + 770 "dialing calls."); 771 rejectCallAndLog(incomingCall, result); 772 } 773 } else if (result.shouldScreenViaAudio) { 774 Log.i(this, "onCallFilteringCompleted: starting background audio processing"); 775 answerCallForAudioProcessing(incomingCall); 776 incomingCall.setAudioProcessingRequestingApp(result.mCallScreeningAppName); 777 } else if (result.shouldSilence) { 778 Log.i(this, "onCallFilteringCompleted: setting the call to silent ringing state"); 779 incomingCall.setSilentRingingRequested(true); 780 addCall(incomingCall); 781 } else { 782 addCall(incomingCall); 783 } 784 } else { 785 if (result.shouldReject) { 786 Log.i(this, "onCallFilteringCompleted: blocked call, rejecting."); 787 incomingCall.reject(false, null); 788 } 789 if (result.shouldAddToCallLog) { 790 Log.i(this, "onCallScreeningCompleted: blocked call, adding to call log."); 791 if (result.shouldShowNotification) { 792 Log.w(this, "onCallScreeningCompleted: blocked call, showing notification."); 793 } 794 mCallLogManager.logCall(incomingCall, Calls.BLOCKED_TYPE, 795 result.shouldShowNotification, result); 796 } else if (result.shouldShowNotification) { 797 Log.i(this, "onCallScreeningCompleted: blocked call, showing notification."); 798 mMissedCallNotifier.showMissedCallNotification( 799 new MissedCallNotifier.CallInfo(incomingCall)); 800 } 801 } 802 } 803 804 /** 805 * In the event that the maximum supported calls of a given type is reached, the 806 * default behavior is to reject any additional calls of that type. This checks 807 * if the device is configured to silence instead of reject the call, provided 808 * that the incoming call is from a different source (connection service). 809 */ shouldSilenceInsteadOfReject(Call incomingCall)810 private boolean shouldSilenceInsteadOfReject(Call incomingCall) { 811 if (!mContext.getResources().getBoolean( 812 R.bool.silence_incoming_when_different_service_and_maximum_ringing)) { 813 return false; 814 } 815 816 for (Call call : mCalls) { 817 // Only operate on top-level calls 818 if (call.getParentCall() != null) { 819 continue; 820 } 821 822 if (call.isExternalCall()) { 823 continue; 824 } 825 826 if (call.getConnectionService() == incomingCall.getConnectionService()) { 827 return false; 828 } 829 } 830 831 return true; 832 } 833 834 @Override onFailedIncomingCall(Call call)835 public void onFailedIncomingCall(Call call) { 836 setCallState(call, CallState.DISCONNECTED, "failed incoming call"); 837 call.removeListener(this); 838 } 839 840 @Override onSuccessfulUnknownCall(Call call, int callState)841 public void onSuccessfulUnknownCall(Call call, int callState) { 842 setCallState(call, callState, "successful unknown call"); 843 Log.i(this, "onSuccessfulUnknownCall for call %s", call); 844 addCall(call); 845 } 846 847 @Override onFailedUnknownCall(Call call)848 public void onFailedUnknownCall(Call call) { 849 Log.i(this, "onFailedUnknownCall for call %s", call); 850 setCallState(call, CallState.DISCONNECTED, "failed unknown call"); 851 call.removeListener(this); 852 } 853 854 @Override onRingbackRequested(Call call, boolean ringback)855 public void onRingbackRequested(Call call, boolean ringback) { 856 for (CallsManagerListener listener : mListeners) { 857 listener.onRingbackRequested(call, ringback); 858 } 859 } 860 861 @Override onPostDialWait(Call call, String remaining)862 public void onPostDialWait(Call call, String remaining) { 863 mInCallController.onPostDialWait(call, remaining); 864 } 865 866 @Override onPostDialChar(final Call call, char nextChar)867 public void onPostDialChar(final Call call, char nextChar) { 868 if (PhoneNumberUtils.is12Key(nextChar)) { 869 // Play tone if it is one of the dialpad digits, canceling out the previously queued 870 // up stopTone runnable since playing a new tone automatically stops the previous tone. 871 if (mStopTone != null) { 872 mHandler.removeCallbacks(mStopTone.getRunnableToCancel()); 873 mStopTone.cancel(); 874 } 875 876 mDtmfLocalTonePlayer.playTone(call, nextChar); 877 878 mStopTone = new Runnable("CM.oPDC", mLock) { 879 @Override 880 public void loggedRun() { 881 // Set a timeout to stop the tone in case there isn't another tone to 882 // follow. 883 mDtmfLocalTonePlayer.stopTone(call); 884 } 885 }; 886 mHandler.postDelayed(mStopTone.prepare(), 887 Timeouts.getDelayBetweenDtmfTonesMillis(mContext.getContentResolver())); 888 } else if (nextChar == 0 || nextChar == TelecomManager.DTMF_CHARACTER_WAIT || 889 nextChar == TelecomManager.DTMF_CHARACTER_PAUSE) { 890 // Stop the tone if a tone is playing, removing any other stopTone callbacks since 891 // the previous tone is being stopped anyway. 892 if (mStopTone != null) { 893 mHandler.removeCallbacks(mStopTone.getRunnableToCancel()); 894 mStopTone.cancel(); 895 } 896 mDtmfLocalTonePlayer.stopTone(call); 897 } else { 898 Log.w(this, "onPostDialChar: invalid value %d", nextChar); 899 } 900 } 901 902 @Override onConnectionPropertiesChanged(Call call, boolean didRttChange)903 public void onConnectionPropertiesChanged(Call call, boolean didRttChange) { 904 if (didRttChange) { 905 updateHasActiveRttCall(); 906 } 907 } 908 909 @Override onParentChanged(Call call)910 public void onParentChanged(Call call) { 911 // parent-child relationship affects which call should be foreground, so do an update. 912 updateCanAddCall(); 913 for (CallsManagerListener listener : mListeners) { 914 listener.onIsConferencedChanged(call); 915 } 916 } 917 918 @Override onChildrenChanged(Call call)919 public void onChildrenChanged(Call call) { 920 // parent-child relationship affects which call should be foreground, so do an update. 921 updateCanAddCall(); 922 for (CallsManagerListener listener : mListeners) { 923 listener.onIsConferencedChanged(call); 924 } 925 } 926 927 @Override onConferenceStateChanged(Call call, boolean isConference)928 public void onConferenceStateChanged(Call call, boolean isConference) { 929 // Conference changed whether it is treated as a conference or not. 930 updateCanAddCall(); 931 for (CallsManagerListener listener : mListeners) { 932 listener.onConferenceStateChanged(call, isConference); 933 } 934 } 935 936 @Override onCdmaConferenceSwap(Call call)937 public void onCdmaConferenceSwap(Call call) { 938 // SWAP was executed on a CDMA conference 939 for (CallsManagerListener listener : mListeners) { 940 listener.onCdmaConferenceSwap(call); 941 } 942 } 943 944 @Override onIsVoipAudioModeChanged(Call call)945 public void onIsVoipAudioModeChanged(Call call) { 946 for (CallsManagerListener listener : mListeners) { 947 listener.onIsVoipAudioModeChanged(call); 948 } 949 } 950 951 @Override onVideoStateChanged(Call call, int previousVideoState, int newVideoState)952 public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) { 953 for (CallsManagerListener listener : mListeners) { 954 listener.onVideoStateChanged(call, previousVideoState, newVideoState); 955 } 956 } 957 958 @Override onCanceledViaNewOutgoingCallBroadcast(final Call call, long disconnectionTimeout)959 public boolean onCanceledViaNewOutgoingCallBroadcast(final Call call, 960 long disconnectionTimeout) { 961 mPendingCallsToDisconnect.add(call); 962 mHandler.postDelayed(new Runnable("CM.oCVNOCB", mLock) { 963 @Override 964 public void loggedRun() { 965 if (mPendingCallsToDisconnect.remove(call)) { 966 Log.i(this, "Delayed disconnection of call: %s", call); 967 call.disconnect(); 968 } 969 } 970 }.prepare(), disconnectionTimeout); 971 972 return true; 973 } 974 975 /** 976 * Handles changes to the {@link Connection.VideoProvider} for a call. Adds the 977 * {@link CallsManager} as a listener for the {@link VideoProviderProxy} which is created 978 * in {@link Call#setVideoProvider(IVideoProvider)}. This allows the {@link CallsManager} to 979 * respond to callbacks from the {@link VideoProviderProxy}. 980 * 981 * @param call The call. 982 */ 983 @Override onVideoCallProviderChanged(Call call)984 public void onVideoCallProviderChanged(Call call) { 985 VideoProviderProxy videoProviderProxy = call.getVideoProviderProxy(); 986 987 if (videoProviderProxy == null) { 988 return; 989 } 990 991 videoProviderProxy.addListener(this); 992 } 993 994 /** 995 * Handles session modification requests received via the {@link TelecomVideoCallCallback} for 996 * a call. Notifies listeners of the {@link CallsManager.CallsManagerListener} of the session 997 * modification request. 998 * 999 * @param call The call. 1000 * @param videoProfile The {@link VideoProfile}. 1001 */ 1002 @Override onSessionModifyRequestReceived(Call call, VideoProfile videoProfile)1003 public void onSessionModifyRequestReceived(Call call, VideoProfile videoProfile) { 1004 int videoState = videoProfile != null ? videoProfile.getVideoState() : 1005 VideoProfile.STATE_AUDIO_ONLY; 1006 Log.v(TAG, "onSessionModifyRequestReceived : videoProfile = " + VideoProfile 1007 .videoStateToString(videoState)); 1008 1009 for (CallsManagerListener listener : mListeners) { 1010 listener.onSessionModifyRequestReceived(call, videoProfile); 1011 } 1012 } 1013 getCalls()1014 public Collection<Call> getCalls() { 1015 return Collections.unmodifiableCollection(mCalls); 1016 } 1017 1018 /** 1019 * Play or stop a call hold tone for a call. Triggered via 1020 * {@link Connection#sendConnectionEvent(String)} when the 1021 * {@link Connection#EVENT_ON_HOLD_TONE_START} event or 1022 * {@link Connection#EVENT_ON_HOLD_TONE_STOP} event is passed through to the 1023 * 1024 * @param call The call which requested the hold tone. 1025 */ 1026 @Override onHoldToneRequested(Call call)1027 public void onHoldToneRequested(Call call) { 1028 for (CallsManagerListener listener : mListeners) { 1029 listener.onHoldToneRequested(call); 1030 } 1031 } 1032 1033 /** 1034 * A {@link Call} managed by the {@link CallsManager} has requested a handover to another 1035 * {@link PhoneAccount}. 1036 * @param call The call. 1037 * @param handoverTo The {@link PhoneAccountHandle} to handover the call to. 1038 * @param videoState The desired video state of the call after handover. 1039 * @param extras 1040 */ 1041 @Override onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy)1042 public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, 1043 Bundle extras, boolean isLegacy) { 1044 if (isLegacy) { 1045 requestHandoverViaEvents(call, handoverTo, videoState, extras); 1046 } else { 1047 requestHandover(call, handoverTo, videoState, extras); 1048 } 1049 } 1050 1051 @VisibleForTesting getForegroundCall()1052 public Call getForegroundCall() { 1053 if (mCallAudioManager == null) { 1054 // Happens when getForegroundCall is called before full initialization. 1055 return null; 1056 } 1057 return mCallAudioManager.getForegroundCall(); 1058 } 1059 1060 @Override onCallHoldFailed(Call call)1061 public void onCallHoldFailed(Call call) { 1062 markAllAnsweredCallAsRinging(call, "hold"); 1063 } 1064 1065 @Override onCallSwitchFailed(Call call)1066 public void onCallSwitchFailed(Call call) { 1067 markAllAnsweredCallAsRinging(call, "switch"); 1068 } 1069 markAllAnsweredCallAsRinging(Call call, String actionName)1070 private void markAllAnsweredCallAsRinging(Call call, String actionName) { 1071 // Normally, we don't care whether a call hold or switch has failed. 1072 // However, if a call was held or switched in order to answer an incoming call, that 1073 // incoming call needs to be brought out of the ANSWERED state so that the user can 1074 // try the operation again. 1075 for (Call call1 : mCalls) { 1076 if (call1 != call && call1.getState() == CallState.ANSWERED) { 1077 setCallState(call1, CallState.RINGING, actionName + " failed on other call"); 1078 } 1079 } 1080 } 1081 1082 @Override getCurrentUserHandle()1083 public UserHandle getCurrentUserHandle() { 1084 return mCurrentUserHandle; 1085 } 1086 getCallAudioManager()1087 public CallAudioManager getCallAudioManager() { 1088 return mCallAudioManager; 1089 } 1090 getInCallController()1091 InCallController getInCallController() { 1092 return mInCallController; 1093 } 1094 getEmergencyCallHelper()1095 EmergencyCallHelper getEmergencyCallHelper() { 1096 return mEmergencyCallHelper; 1097 } 1098 getDefaultDialerCache()1099 public DefaultDialerCache getDefaultDialerCache() { 1100 return mDefaultDialerCache; 1101 } 1102 1103 @VisibleForTesting getPhoneAccountListener()1104 public PhoneAccountRegistrar.Listener getPhoneAccountListener() { 1105 return mPhoneAccountListener; 1106 } 1107 hasEmergencyRttCall()1108 public boolean hasEmergencyRttCall() { 1109 for (Call call : mCalls) { 1110 if (call.isEmergencyCall() && call.isRttCall()) { 1111 return true; 1112 } 1113 } 1114 return false; 1115 } 1116 1117 @VisibleForTesting hasOnlyDisconnectedCalls()1118 public boolean hasOnlyDisconnectedCalls() { 1119 if (mCalls.size() == 0) { 1120 return false; 1121 } 1122 for (Call call : mCalls) { 1123 if (!call.isDisconnected()) { 1124 return false; 1125 } 1126 } 1127 return true; 1128 } 1129 hasVideoCall()1130 public boolean hasVideoCall() { 1131 for (Call call : mCalls) { 1132 if (VideoProfile.isVideo(call.getVideoState())) { 1133 return true; 1134 } 1135 } 1136 return false; 1137 } 1138 1139 @VisibleForTesting getAudioState()1140 public CallAudioState getAudioState() { 1141 return mCallAudioManager.getCallAudioState(); 1142 } 1143 isTtySupported()1144 boolean isTtySupported() { 1145 return mTtyManager.isTtySupported(); 1146 } 1147 getCurrentTtyMode()1148 int getCurrentTtyMode() { 1149 return mTtyManager.getCurrentTtyMode(); 1150 } 1151 1152 @VisibleForTesting addListener(CallsManagerListener listener)1153 public void addListener(CallsManagerListener listener) { 1154 mListeners.add(listener); 1155 } 1156 1157 @VisibleForTesting removeListener(CallsManagerListener listener)1158 public void removeListener(CallsManagerListener listener) { 1159 mListeners.remove(listener); 1160 } 1161 processIncomingConference(PhoneAccountHandle phoneAccountHandle, Bundle extras)1162 void processIncomingConference(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 1163 Log.d(this, "processIncomingCallConference"); 1164 processIncomingCallIntent(phoneAccountHandle, extras, true); 1165 } 1166 1167 /** 1168 * Starts the process to attach the call to a connection service. 1169 * 1170 * @param phoneAccountHandle The phone account which contains the component name of the 1171 * connection service to use for this call. 1172 * @param extras The optional extras Bundle passed with the intent used for the incoming call. 1173 */ processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras)1174 void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 1175 processIncomingCallIntent(phoneAccountHandle, extras, false); 1176 } 1177 processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras, boolean isConference)1178 void processIncomingCallIntent(PhoneAccountHandle phoneAccountHandle, Bundle extras, 1179 boolean isConference) { 1180 Log.d(this, "processIncomingCallIntent"); 1181 boolean isHandover = extras.getBoolean(TelecomManager.EXTRA_IS_HANDOVER); 1182 Uri handle = extras.getParcelable(TelecomManager.EXTRA_INCOMING_CALL_ADDRESS); 1183 if (handle == null) { 1184 // Required for backwards compatibility 1185 handle = extras.getParcelable(TelephonyManager.EXTRA_INCOMING_NUMBER); 1186 } 1187 Call call = new Call( 1188 getNextCallId(), 1189 mContext, 1190 this, 1191 mLock, 1192 mConnectionServiceRepository, 1193 mPhoneNumberUtilsAdapter, 1194 handle, 1195 null /* gatewayInfo */, 1196 null /* connectionManagerPhoneAccount */, 1197 phoneAccountHandle, 1198 Call.CALL_DIRECTION_INCOMING /* callDirection */, 1199 false /* forceAttachToExistingConnection */, 1200 isConference, /* isConference */ 1201 mClockProxy, 1202 mToastFactory); 1203 1204 // Ensure new calls related to self-managed calls/connections are set as such. This will 1205 // be overridden when the actual connection is returned in startCreateConnection, however 1206 // doing this now ensures the logs and any other logic will treat this call as self-managed 1207 // from the moment it is created. 1208 PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked( 1209 phoneAccountHandle); 1210 if (phoneAccount != null) { 1211 call.setIsSelfManaged(phoneAccount.isSelfManaged()); 1212 if (call.isSelfManaged()) { 1213 // Self managed calls will always be voip audio mode. 1214 call.setIsVoipAudioMode(true); 1215 } else { 1216 // Incoming call is managed, the active call is self-managed and can't be held. 1217 // We need to set extras on it to indicate whether answering will cause a 1218 // active self-managed call to drop. 1219 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); 1220 if (activeCall != null && !canHold(activeCall) && activeCall.isSelfManaged()) { 1221 Bundle dropCallExtras = new Bundle(); 1222 dropCallExtras.putBoolean(Connection.EXTRA_ANSWERING_DROPS_FG_CALL, true); 1223 1224 // Include the name of the app which will drop the call. 1225 CharSequence droppedApp = activeCall.getTargetPhoneAccountLabel(); 1226 dropCallExtras.putCharSequence( 1227 Connection.EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME, droppedApp); 1228 Log.i(this, "Incoming managed call will drop %s call.", droppedApp); 1229 call.putExtras(Call.SOURCE_CONNECTION_SERVICE, dropCallExtras); 1230 } 1231 } 1232 1233 Bundle phoneAccountExtras = phoneAccount.getExtras(); 1234 if (phoneAccountExtras != null 1235 && phoneAccountExtras.getBoolean( 1236 PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) { 1237 Log.d(this, "processIncomingCallIntent: defaulting to voip mode for call %s", 1238 call.getId()); 1239 call.setIsVoipAudioMode(true); 1240 } 1241 } 1242 1243 boolean isRttSettingOn = isRttSettingOn(phoneAccountHandle); 1244 if (isRttSettingOn || 1245 extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, false)) { 1246 Log.i(this, "Incoming call requesting RTT, rtt setting is %b", isRttSettingOn); 1247 call.createRttStreams(); 1248 // Even if the phone account doesn't support RTT yet, the connection manager might 1249 // change that. Set this to check it later. 1250 call.setRequestedToStartWithRtt(); 1251 } 1252 // If the extras specifies a video state, set it on the call if the PhoneAccount supports 1253 // video. 1254 int videoState = VideoProfile.STATE_AUDIO_ONLY; 1255 if (extras.containsKey(TelecomManager.EXTRA_INCOMING_VIDEO_STATE) && 1256 phoneAccount != null && phoneAccount.hasCapabilities( 1257 PhoneAccount.CAPABILITY_VIDEO_CALLING)) { 1258 videoState = extras.getInt(TelecomManager.EXTRA_INCOMING_VIDEO_STATE); 1259 call.setVideoState(videoState); 1260 } 1261 1262 call.initAnalytics(); 1263 if (getForegroundCall() != null) { 1264 getForegroundCall().getAnalytics().setCallIsInterrupted(true); 1265 call.getAnalytics().setCallIsAdditional(true); 1266 } 1267 setIntentExtrasAndStartTime(call, extras); 1268 // TODO: Move this to be a part of addCall() 1269 call.addListener(this); 1270 1271 if (extras.containsKey(TelecomManager.EXTRA_CALL_DISCONNECT_MESSAGE)) { 1272 String disconnectMessage = extras.getString(TelecomManager.EXTRA_CALL_DISCONNECT_MESSAGE); 1273 Log.i(this, "processIncomingCallIntent Disconnect message " + disconnectMessage); 1274 } 1275 1276 boolean isHandoverAllowed = true; 1277 if (isHandover) { 1278 if (!isHandoverInProgress() && 1279 isHandoverToPhoneAccountSupported(phoneAccountHandle)) { 1280 final String handleScheme = handle.getSchemeSpecificPart(); 1281 Call fromCall = mCalls.stream() 1282 .filter((c) -> mPhoneNumberUtilsAdapter.isSamePhoneNumber( 1283 (c.getHandle() == null 1284 ? null : c.getHandle().getSchemeSpecificPart()), 1285 handleScheme)) 1286 .findFirst() 1287 .orElse(null); 1288 if (fromCall != null) { 1289 if (!isHandoverFromPhoneAccountSupported(fromCall.getTargetPhoneAccount())) { 1290 Log.w(this, "processIncomingCallIntent: From account doesn't support " + 1291 "handover."); 1292 isHandoverAllowed = false; 1293 } 1294 } else { 1295 Log.w(this, "processIncomingCallIntent: handover fail; can't find from call."); 1296 isHandoverAllowed = false; 1297 } 1298 1299 if (isHandoverAllowed) { 1300 // Link the calls so we know we're handing over. 1301 fromCall.setHandoverDestinationCall(call); 1302 call.setHandoverSourceCall(fromCall); 1303 call.setHandoverState(HandoverState.HANDOVER_TO_STARTED); 1304 fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); 1305 Log.addEvent(fromCall, LogUtils.Events.START_HANDOVER, 1306 "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId()); 1307 Log.addEvent(call, LogUtils.Events.START_HANDOVER, 1308 "handOverFrom=%s, handOverTo=%s", fromCall.getId(), call.getId()); 1309 if (isSpeakerEnabledForVideoCalls() && VideoProfile.isVideo(videoState)) { 1310 // Ensure when the call goes active that it will go to speakerphone if the 1311 // handover to call is a video call. 1312 call.setStartWithSpeakerphoneOn(true); 1313 } 1314 } 1315 } else { 1316 Log.w(this, "processIncomingCallIntent: To account doesn't support handover."); 1317 } 1318 } 1319 1320 if (!isHandoverAllowed || (call.isSelfManaged() && !isIncomingCallPermitted(call, 1321 call.getTargetPhoneAccount()))) { 1322 if (isConference) { 1323 notifyCreateConferenceFailed(phoneAccountHandle, call); 1324 } else { 1325 notifyCreateConnectionFailed(phoneAccountHandle, call); 1326 } 1327 } else if (isInEmergencyCall()) { 1328 // The incoming call is implicitly being rejected so the user does not get any incoming 1329 // call UI during an emergency call. In this case, log the call as missed instead of 1330 // rejected since the user did not explicitly reject. 1331 mCallLogManager.logCall(call, Calls.MISSED_TYPE, 1332 true /*showNotificationForMissedCall*/, null /*CallFilteringResult*/); 1333 if (isConference) { 1334 notifyCreateConferenceFailed(phoneAccountHandle, call); 1335 } else { 1336 notifyCreateConnectionFailed(phoneAccountHandle, call); 1337 } 1338 } else { 1339 call.startCreateConnection(mPhoneAccountRegistrar); 1340 } 1341 } 1342 addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras)1343 void addNewUnknownCall(PhoneAccountHandle phoneAccountHandle, Bundle extras) { 1344 Uri handle = extras.getParcelable(TelecomManager.EXTRA_UNKNOWN_CALL_HANDLE); 1345 Log.i(this, "addNewUnknownCall with handle: %s", Log.pii(handle)); 1346 Call call = new Call( 1347 getNextCallId(), 1348 mContext, 1349 this, 1350 mLock, 1351 mConnectionServiceRepository, 1352 mPhoneNumberUtilsAdapter, 1353 handle, 1354 null /* gatewayInfo */, 1355 null /* connectionManagerPhoneAccount */, 1356 phoneAccountHandle, 1357 Call.CALL_DIRECTION_UNKNOWN /* callDirection */, 1358 // Use onCreateIncomingConnection in TelephonyConnectionService, so that we attach 1359 // to the existing connection instead of trying to create a new one. 1360 true /* forceAttachToExistingConnection */, 1361 false, /* isConference */ 1362 mClockProxy, 1363 mToastFactory); 1364 call.initAnalytics(); 1365 1366 setIntentExtrasAndStartTime(call, extras); 1367 call.addListener(this); 1368 call.startCreateConnection(mPhoneAccountRegistrar); 1369 } 1370 areHandlesEqual(Uri handle1, Uri handle2)1371 private boolean areHandlesEqual(Uri handle1, Uri handle2) { 1372 if (handle1 == null || handle2 == null) { 1373 return handle1 == handle2; 1374 } 1375 1376 if (!TextUtils.equals(handle1.getScheme(), handle2.getScheme())) { 1377 return false; 1378 } 1379 1380 final String number1 = PhoneNumberUtils.normalizeNumber(handle1.getSchemeSpecificPart()); 1381 final String number2 = PhoneNumberUtils.normalizeNumber(handle2.getSchemeSpecificPart()); 1382 return TextUtils.equals(number1, number2); 1383 } 1384 reuseOutgoingCall(Uri handle)1385 private Call reuseOutgoingCall(Uri handle) { 1386 // Check to see if we can reuse any of the calls that are waiting to disconnect. 1387 // See {@link Call#abort} and {@link #onCanceledViaNewOutgoingCall} for more information. 1388 Call reusedCall = null; 1389 for (Iterator<Call> callIter = mPendingCallsToDisconnect.iterator(); callIter.hasNext();) { 1390 Call pendingCall = callIter.next(); 1391 if (reusedCall == null && areHandlesEqual(pendingCall.getHandle(), handle)) { 1392 callIter.remove(); 1393 Log.i(this, "Reusing disconnected call %s", pendingCall); 1394 reusedCall = pendingCall; 1395 } else { 1396 Log.i(this, "Not reusing disconnected call %s", pendingCall); 1397 callIter.remove(); 1398 pendingCall.disconnect(); 1399 } 1400 } 1401 1402 return reusedCall; 1403 } 1404 1405 /** 1406 * Kicks off the first steps to creating an outgoing call. 1407 * 1408 * For managed connections, this is the first step to launching the Incall UI. 1409 * For self-managed connections, we don't expect the Incall UI to launch, but this is still a 1410 * first step in getting the self-managed ConnectionService to create the connection. 1411 * @param handle Handle to connect the call with. 1412 * @param requestedAccountHandle The phone account which contains the component name of the 1413 * connection service to use for this call. 1414 * @param extras The optional extras Bundle passed with the intent used for the incoming call. 1415 * @param initiatingUser {@link UserHandle} of user that place the outgoing call. 1416 * @param originalIntent 1417 * @param callingPackage the package name of the app which initiated the outgoing call. 1418 */ 1419 @VisibleForTesting 1420 public @NonNull startOutgoingCall(Uri handle, PhoneAccountHandle requestedAccountHandle, Bundle extras, UserHandle initiatingUser, Intent originalIntent, String callingPackage)1421 CompletableFuture<Call> startOutgoingCall(Uri handle, 1422 PhoneAccountHandle requestedAccountHandle, 1423 Bundle extras, UserHandle initiatingUser, Intent originalIntent, 1424 String callingPackage) { 1425 final List<Uri> callee = new ArrayList<>(); 1426 callee.add(handle); 1427 return startOutgoingCall(callee, requestedAccountHandle, extras, initiatingUser, 1428 originalIntent, callingPackage, false); 1429 } 1430 startOutgoingCall(List<Uri> participants, PhoneAccountHandle requestedAccountHandle, Bundle extras, UserHandle initiatingUser, Intent originalIntent, String callingPackage, boolean isConference)1431 private CompletableFuture<Call> startOutgoingCall(List<Uri> participants, 1432 PhoneAccountHandle requestedAccountHandle, 1433 Bundle extras, UserHandle initiatingUser, Intent originalIntent, 1434 String callingPackage, boolean isConference) { 1435 boolean isReusedCall; 1436 Uri handle = isConference ? Uri.parse("tel:conf-factory") : participants.get(0); 1437 Call call = reuseOutgoingCall(handle); 1438 1439 PhoneAccount account = 1440 mPhoneAccountRegistrar.getPhoneAccount(requestedAccountHandle, initiatingUser); 1441 boolean isSelfManaged = account != null && account.isSelfManaged(); 1442 1443 // Create a call with original handle. The handle may be changed when the call is attached 1444 // to a connection service, but in most cases will remain the same. 1445 if (call == null) { 1446 call = new Call(getNextCallId(), mContext, 1447 this, 1448 mLock, 1449 mConnectionServiceRepository, 1450 mPhoneNumberUtilsAdapter, 1451 handle, 1452 isConference ? participants : null, 1453 null /* gatewayInfo */, 1454 null /* connectionManagerPhoneAccount */, 1455 null /* requestedAccountHandle */, 1456 Call.CALL_DIRECTION_OUTGOING /* callDirection */, 1457 false /* forceAttachToExistingConnection */, 1458 isConference, /* isConference */ 1459 mClockProxy, 1460 mToastFactory); 1461 call.initAnalytics(callingPackage); 1462 1463 // Ensure new calls related to self-managed calls/connections are set as such. This 1464 // will be overridden when the actual connection is returned in startCreateConnection, 1465 // however doing this now ensures the logs and any other logic will treat this call as 1466 // self-managed from the moment it is created. 1467 call.setIsSelfManaged(isSelfManaged); 1468 if (isSelfManaged) { 1469 // Self-managed calls will ALWAYS use voip audio mode. 1470 call.setIsVoipAudioMode(true); 1471 } 1472 call.setInitiatingUser(initiatingUser); 1473 isReusedCall = false; 1474 } else { 1475 isReusedCall = true; 1476 } 1477 1478 int videoState = VideoProfile.STATE_AUDIO_ONLY; 1479 if (extras != null) { 1480 // Set the video state on the call early so that when it is added to the InCall UI the 1481 // UI knows to configure itself as a video call immediately. 1482 videoState = extras.getInt(TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE, 1483 VideoProfile.STATE_AUDIO_ONLY); 1484 1485 // If this is an emergency video call, we need to check if the phone account supports 1486 // emergency video calling. 1487 // Also, ensure we don't try to place an outgoing call with video if video is not 1488 // supported. 1489 if (VideoProfile.isVideo(videoState)) { 1490 if (call.isEmergencyCall() && account != null && 1491 !account.hasCapabilities(PhoneAccount.CAPABILITY_EMERGENCY_VIDEO_CALLING)) { 1492 // Phone account doesn't support emergency video calling, so fallback to 1493 // audio-only now to prevent the InCall UI from setting up video surfaces 1494 // needlessly. 1495 Log.i(this, "startOutgoingCall - emergency video calls not supported; " + 1496 "falling back to audio-only"); 1497 videoState = VideoProfile.STATE_AUDIO_ONLY; 1498 } else if (account != null && 1499 !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) { 1500 // Phone account doesn't support video calling, so fallback to audio-only. 1501 Log.i(this, "startOutgoingCall - video calls not supported; fallback to " + 1502 "audio-only."); 1503 videoState = VideoProfile.STATE_AUDIO_ONLY; 1504 } 1505 } 1506 1507 call.setVideoState(videoState); 1508 } 1509 1510 final int finalVideoState = videoState; 1511 final Call finalCall = call; 1512 Handler outgoingCallHandler = new Handler(Looper.getMainLooper()); 1513 // Create a empty CompletableFuture and compose it with findOutgoingPhoneAccount to get 1514 // a first guess at the list of suitable outgoing PhoneAccounts. 1515 // findOutgoingPhoneAccount returns a CompletableFuture which is either already complete 1516 // (in the case where we don't need to do the per-contact lookup) or a CompletableFuture 1517 // that completes once the contact lookup via CallerInfoLookupHelper is complete. 1518 CompletableFuture<List<PhoneAccountHandle>> accountsForCall = 1519 CompletableFuture.completedFuture((Void) null).thenComposeAsync((x) -> 1520 findOutgoingCallPhoneAccount(requestedAccountHandle, handle, 1521 VideoProfile.isVideo(finalVideoState), 1522 finalCall.isEmergencyCall(), initiatingUser, 1523 isConference), 1524 new LoggedHandlerExecutor(outgoingCallHandler, "CM.fOCP", mLock)); 1525 1526 // This is a block of code that executes after the list of potential phone accts has been 1527 // retrieved. 1528 CompletableFuture<List<PhoneAccountHandle>> setAccountHandle = 1529 accountsForCall.whenCompleteAsync((potentialPhoneAccounts, exception) -> { 1530 Log.i(CallsManager.this, "set outgoing call phone acct stage"); 1531 PhoneAccountHandle phoneAccountHandle; 1532 if (potentialPhoneAccounts.size() == 1) { 1533 phoneAccountHandle = potentialPhoneAccounts.get(0); 1534 } else { 1535 phoneAccountHandle = null; 1536 } 1537 finalCall.setTargetPhoneAccount(phoneAccountHandle); 1538 }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.sOCPA", mLock)); 1539 1540 1541 // This composes the future containing the potential phone accounts with code that queries 1542 // the suggestion service if necessary (i.e. if the list is longer than 1). 1543 // If the suggestion service is queried, the inner lambda will return a future that 1544 // completes when the suggestion service calls the callback. 1545 CompletableFuture<List<PhoneAccountSuggestion>> suggestionFuture = accountsForCall. 1546 thenComposeAsync(potentialPhoneAccounts -> { 1547 Log.i(CallsManager.this, "call outgoing call suggestion service stage"); 1548 if (potentialPhoneAccounts.size() == 1) { 1549 PhoneAccountSuggestion suggestion = 1550 new PhoneAccountSuggestion(potentialPhoneAccounts.get(0), 1551 PhoneAccountSuggestion.REASON_NONE, true); 1552 return CompletableFuture.completedFuture( 1553 Collections.singletonList(suggestion)); 1554 } 1555 return PhoneAccountSuggestionHelper.bindAndGetSuggestions(mContext, 1556 finalCall.getHandle(), potentialPhoneAccounts); 1557 }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.cOCSS", mLock)); 1558 1559 1560 // This future checks the status of existing calls and attempts to make room for the 1561 // outgoing call. The future returned by the inner method will usually be pre-completed -- 1562 // we only pause here if user interaction is required to disconnect a self-managed call. 1563 // It runs after the account handle is set, independently of the phone account suggestion 1564 // future. 1565 CompletableFuture<Call> makeRoomForCall = setAccountHandle.thenComposeAsync( 1566 potentialPhoneAccounts -> { 1567 Log.i(CallsManager.this, "make room for outgoing call stage"); 1568 if (isPotentialInCallMMICode(handle) && !isSelfManaged) { 1569 return CompletableFuture.completedFuture(finalCall); 1570 } 1571 // If a call is being reused, then it has already passed the 1572 // makeRoomForOutgoingCall check once and will fail the second time due to the 1573 // call transitioning into the CONNECTING state. 1574 if (isReusedCall) { 1575 return CompletableFuture.completedFuture(finalCall); 1576 } 1577 1578 // If we can not supportany more active calls, our options are to move a call 1579 // to hold, disconnect a call, or cancel this call altogether. 1580 boolean isRoomForCall = finalCall.isEmergencyCall() ? 1581 makeRoomForOutgoingEmergencyCall(finalCall) : 1582 makeRoomForOutgoingCall(finalCall); 1583 if (!isRoomForCall) { 1584 Call foregroundCall = getForegroundCall(); 1585 Log.d(CallsManager.this, "No more room for outgoing call %s ", finalCall); 1586 if (foregroundCall.isSelfManaged()) { 1587 // If the ongoing call is a self-managed call, then prompt the user to 1588 // ask if they'd like to disconnect their ongoing call and place the 1589 // outgoing call. 1590 Log.i(CallsManager.this, "Prompting user to disconnect " 1591 + "self-managed call"); 1592 finalCall.setOriginalCallIntent(originalIntent); 1593 CompletableFuture<Call> completionFuture = new CompletableFuture<>(); 1594 startCallConfirmation(finalCall, completionFuture); 1595 return completionFuture; 1596 } else { 1597 // If the ongoing call is a managed call, we will prevent the outgoing 1598 // call from dialing. 1599 if (isConference) { 1600 notifyCreateConferenceFailed(finalCall.getTargetPhoneAccount(), 1601 finalCall); 1602 } else { 1603 notifyCreateConnectionFailed( 1604 finalCall.getTargetPhoneAccount(), finalCall); 1605 } 1606 } 1607 Log.i(CallsManager.this, "Aborting call since there's no room"); 1608 return CompletableFuture.completedFuture(null); 1609 } 1610 return CompletableFuture.completedFuture(finalCall); 1611 }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSMCP", mLock)); 1612 1613 // The outgoing call can be placed, go forward. This future glues together the results of 1614 // the account suggestion stage and the make room for call stage. 1615 CompletableFuture<Pair<Call, List<PhoneAccountSuggestion>>> preSelectStage = 1616 makeRoomForCall.thenCombine(suggestionFuture, Pair::create); 1617 mLatestPreAccountSelectionFuture = preSelectStage; 1618 1619 // This future takes the list of suggested accounts and the call and determines if more 1620 // user interaction in the form of a phone account selection screen is needed. If so, it 1621 // will set the call to SELECT_PHONE_ACCOUNT, add it to our internal list/send it to dialer, 1622 // and then execution will pause pending the dialer calling phoneAccountSelected. 1623 CompletableFuture<Pair<Call, PhoneAccountHandle>> dialerSelectPhoneAccountFuture = 1624 preSelectStage.thenComposeAsync( 1625 (args) -> { 1626 Log.i(CallsManager.this, "dialer phone acct select stage"); 1627 Call callToPlace = args.first; 1628 List<PhoneAccountSuggestion> accountSuggestions = args.second; 1629 if (callToPlace == null) { 1630 return CompletableFuture.completedFuture(null); 1631 } 1632 if (accountSuggestions == null || accountSuggestions.isEmpty()) { 1633 Log.i(CallsManager.this, "Aborting call since there are no" 1634 + " available accounts."); 1635 showErrorMessage(R.string.cant_call_due_to_no_supported_service); 1636 return CompletableFuture.completedFuture(null); 1637 } 1638 boolean needsAccountSelection = accountSuggestions.size() > 1 1639 && !callToPlace.isEmergencyCall() && !isSelfManaged; 1640 if (!needsAccountSelection) { 1641 return CompletableFuture.completedFuture(Pair.create(callToPlace, 1642 accountSuggestions.get(0).getPhoneAccountHandle())); 1643 } 1644 // This is the state where the user is expected to select an account 1645 callToPlace.setState(CallState.SELECT_PHONE_ACCOUNT, 1646 "needs account selection"); 1647 // Create our own instance to modify (since extras may be Bundle.EMPTY) 1648 Bundle newExtras = new Bundle(extras); 1649 List<PhoneAccountHandle> accountsFromSuggestions = accountSuggestions 1650 .stream() 1651 .map(PhoneAccountSuggestion::getPhoneAccountHandle) 1652 .collect(Collectors.toList()); 1653 newExtras.putParcelableList( 1654 android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS, 1655 accountsFromSuggestions); 1656 newExtras.putParcelableList( 1657 android.telecom.Call.EXTRA_SUGGESTED_PHONE_ACCOUNTS, 1658 accountSuggestions); 1659 // Set a future in place so that we can proceed once the dialer replies. 1660 mPendingAccountSelection = new CompletableFuture<>(); 1661 callToPlace.setIntentExtras(newExtras); 1662 1663 addCall(callToPlace); 1664 return mPendingAccountSelection; 1665 }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.dSPA", mLock)); 1666 1667 // Potentially perform call identification for dialed TEL scheme numbers. 1668 if (PhoneAccount.SCHEME_TEL.equals(handle.getScheme())) { 1669 // Perform an asynchronous contacts lookup in this stage; ensure post-dial digits are 1670 // not included. 1671 CompletableFuture<Pair<Uri, CallerInfo>> contactLookupFuture = 1672 mCallerInfoLookupHelper.startLookup(Uri.fromParts(handle.getScheme(), 1673 PhoneNumberUtils.extractNetworkPortion(handle.getSchemeSpecificPart()), 1674 null)); 1675 1676 // Once the phone account selection stage has completed, we can handle the results from 1677 // that with the contacts lookup in order to determine if we should lookup bind to the 1678 // CallScreeningService in order for it to potentially provide caller ID. 1679 dialerSelectPhoneAccountFuture.thenAcceptBothAsync(contactLookupFuture, 1680 (callPhoneAccountHandlePair, uriCallerInfoPair) -> { 1681 Call theCall = callPhoneAccountHandlePair.first; 1682 boolean isInContacts = uriCallerInfoPair.second != null 1683 && uriCallerInfoPair.second.contactExists; 1684 Log.d(CallsManager.this, "outgoingCallIdStage: isInContacts=%s", 1685 isInContacts); 1686 1687 // We only want to provide a CallScreeningService with a call if its not in 1688 // contacts or the package has READ_CONTACT permission. 1689 PackageManager packageManager = mContext.getPackageManager(); 1690 int permission = packageManager.checkPermission( 1691 Manifest.permission.READ_CONTACTS, 1692 mRoleManagerAdapter.getDefaultCallScreeningApp()); 1693 Log.d(CallsManager.this, 1694 "default call screening service package %s has permissions=%s", 1695 mRoleManagerAdapter.getDefaultCallScreeningApp(), 1696 permission == PackageManager.PERMISSION_GRANTED); 1697 if ((!isInContacts) || (permission == PackageManager.PERMISSION_GRANTED)) { 1698 bindForOutgoingCallerId(theCall); 1699 } 1700 }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pCSB", mLock)); 1701 } 1702 1703 // Finally, after all user interaction is complete, we execute this code to finish setting 1704 // up the outgoing call. The inner method always returns a completed future containing the 1705 // call that we've finished setting up. 1706 mLatestPostSelectionProcessingFuture = dialerSelectPhoneAccountFuture 1707 .thenComposeAsync(args -> { 1708 if (args == null) { 1709 return CompletableFuture.completedFuture(null); 1710 } 1711 Log.i(CallsManager.this, "post acct selection stage"); 1712 Call callToUse = args.first; 1713 PhoneAccountHandle phoneAccountHandle = args.second; 1714 PhoneAccount accountToUse = mPhoneAccountRegistrar 1715 .getPhoneAccount(phoneAccountHandle, initiatingUser); 1716 callToUse.setTargetPhoneAccount(phoneAccountHandle); 1717 if (accountToUse != null && accountToUse.getExtras() != null) { 1718 if (accountToUse.getExtras() 1719 .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) { 1720 Log.d(this, "startOutgoingCall: defaulting to voip mode for call %s", 1721 callToUse.getId()); 1722 callToUse.setIsVoipAudioMode(true); 1723 } 1724 } 1725 1726 callToUse.setState( 1727 CallState.CONNECTING, 1728 phoneAccountHandle == null ? "no-handle" 1729 : phoneAccountHandle.toString()); 1730 1731 boolean isVoicemail = isVoicemail(callToUse.getHandle(), accountToUse); 1732 1733 boolean isRttSettingOn = isRttSettingOn(phoneAccountHandle); 1734 if (!isVoicemail && (isRttSettingOn || (extras != null 1735 && extras.getBoolean(TelecomManager.EXTRA_START_CALL_WITH_RTT, 1736 false)))) { 1737 Log.d(this, "Outgoing call requesting RTT, rtt setting is %b", 1738 isRttSettingOn); 1739 if (callToUse.isEmergencyCall() || (accountToUse != null 1740 && accountToUse.hasCapabilities(PhoneAccount.CAPABILITY_RTT))) { 1741 // If the call requested RTT and it's an emergency call, ignore the 1742 // capability and hope that the modem will deal with it somehow. 1743 callToUse.createRttStreams(); 1744 } 1745 // Even if the phone account doesn't support RTT yet, 1746 // the connection manager might change that. Set this to check it later. 1747 callToUse.setRequestedToStartWithRtt(); 1748 } 1749 1750 setIntentExtrasAndStartTime(callToUse, extras); 1751 setCallSourceToAnalytics(callToUse, originalIntent); 1752 1753 if (isPotentialMMICode(handle) && !isSelfManaged) { 1754 // Do not add the call if it is a potential MMI code. 1755 callToUse.addListener(this); 1756 } else if (!mCalls.contains(callToUse)) { 1757 // We check if mCalls already contains the call because we could 1758 // potentially be reusing 1759 // a call which was previously added (See {@link #reuseOutgoingCall}). 1760 addCall(callToUse); 1761 } 1762 return CompletableFuture.completedFuture(callToUse); 1763 }, new LoggedHandlerExecutor(outgoingCallHandler, "CM.pASP", mLock)); 1764 return mLatestPostSelectionProcessingFuture; 1765 } 1766 startConference(List<Uri> participants, Bundle clientExtras, String callingPackage, UserHandle initiatingUser)1767 public void startConference(List<Uri> participants, Bundle clientExtras, String callingPackage, 1768 UserHandle initiatingUser) { 1769 1770 if (clientExtras == null) { 1771 clientExtras = new Bundle(); 1772 } 1773 1774 PhoneAccountHandle phoneAccountHandle = clientExtras.getParcelable( 1775 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE); 1776 CompletableFuture<Call> callFuture = startOutgoingCall(participants, phoneAccountHandle, 1777 clientExtras, initiatingUser, null/* originalIntent */, callingPackage, 1778 true/* isconference*/); 1779 1780 final boolean speakerphoneOn = clientExtras.getBoolean( 1781 TelecomManager.EXTRA_START_CALL_WITH_SPEAKERPHONE); 1782 final int videoState = clientExtras.getInt( 1783 TelecomManager.EXTRA_START_CALL_WITH_VIDEO_STATE); 1784 1785 final Session logSubsession = Log.createSubsession(); 1786 callFuture.thenAccept((call) -> { 1787 if (call != null) { 1788 Log.continueSession(logSubsession, "CM.pOGC"); 1789 try { 1790 placeOutgoingCall(call, call.getHandle(), null/* gatewayInfo */, 1791 speakerphoneOn, videoState); 1792 } finally { 1793 Log.endSession(); 1794 } 1795 } 1796 }); 1797 } 1798 1799 /** 1800 * Performs call identification for an outgoing phone call. 1801 * @param theCall The outgoing call to perform identification. 1802 */ bindForOutgoingCallerId(Call theCall)1803 private void bindForOutgoingCallerId(Call theCall) { 1804 // Find the user chosen call screening app. 1805 String callScreeningApp = 1806 mRoleManagerAdapter.getDefaultCallScreeningApp(); 1807 1808 CompletableFuture future = 1809 new CallScreeningServiceHelper(mContext, 1810 mLock, 1811 callScreeningApp, 1812 new ParcelableCallUtils.Converter(), 1813 mCurrentUserHandle, 1814 theCall, 1815 new AppLabelProxy() { 1816 @Override 1817 public CharSequence getAppLabel(String packageName) { 1818 return Util.getAppLabel(mContext.getPackageManager(), packageName); 1819 } 1820 }).process(); 1821 future.thenApply( v -> { 1822 Log.i(this, "Outgoing caller ID complete"); 1823 return null; 1824 }); 1825 } 1826 1827 /** 1828 * Finds the {@link PhoneAccountHandle}(s) which could potentially be used to place an outgoing 1829 * call. Takes into account the following: 1830 * 1. Any pre-chosen {@link PhoneAccountHandle} which was specified on the 1831 * {@link Intent#ACTION_CALL} intent. If one was chosen it will be used if possible. 1832 * 2. Whether the call is a video call. If the call being placed is a video call, an attempt is 1833 * first made to consider video capable phone accounts. If no video capable phone accounts are 1834 * found, the usual non-video capable phone accounts will be considered. 1835 * 3. Whether there is a user-chosen default phone account; that one will be used if possible. 1836 * 1837 * @param targetPhoneAccountHandle The pre-chosen {@link PhoneAccountHandle} passed in when the 1838 * call was placed. Will be {@code null} if the 1839 * {@link Intent#ACTION_CALL} intent did not specify a target 1840 * phone account. 1841 * @param handle The handle of the outgoing call; used to determine the SIP scheme when matching 1842 * phone accounts. 1843 * @param isVideo {@code true} if the call is a video call, {@code false} otherwise. 1844 * @param isEmergency {@code true} if the call is an emergency call. 1845 * @param initiatingUser The {@link UserHandle} the call is placed on. 1846 * @return 1847 */ 1848 @VisibleForTesting findOutgoingCallPhoneAccount( PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo, boolean isEmergency, UserHandle initiatingUser)1849 public CompletableFuture<List<PhoneAccountHandle>> findOutgoingCallPhoneAccount( 1850 PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo, 1851 boolean isEmergency, UserHandle initiatingUser) { 1852 return findOutgoingCallPhoneAccount(targetPhoneAccountHandle, handle, isVideo, 1853 isEmergency, initiatingUser, false/* isConference */); 1854 } 1855 findOutgoingCallPhoneAccount( PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo, boolean isEmergency, UserHandle initiatingUser, boolean isConference)1856 public CompletableFuture<List<PhoneAccountHandle>> findOutgoingCallPhoneAccount( 1857 PhoneAccountHandle targetPhoneAccountHandle, Uri handle, boolean isVideo, 1858 boolean isEmergency, UserHandle initiatingUser, boolean isConference) { 1859 1860 if (isSelfManaged(targetPhoneAccountHandle, initiatingUser)) { 1861 return CompletableFuture.completedFuture(Arrays.asList(targetPhoneAccountHandle)); 1862 } 1863 1864 List<PhoneAccountHandle> accounts; 1865 // Try to find a potential phone account, taking into account whether this is a video 1866 // call. 1867 accounts = constructPossiblePhoneAccounts(handle, initiatingUser, isVideo, isEmergency, 1868 isConference); 1869 if (isVideo && accounts.size() == 0) { 1870 // Placing a video call but no video capable accounts were found, so consider any 1871 // call capable accounts (we can fallback to audio). 1872 accounts = constructPossiblePhoneAccounts(handle, initiatingUser, 1873 false /* isVideo */, isEmergency /* isEmergency */, isConference); 1874 } 1875 Log.v(this, "findOutgoingCallPhoneAccount: accounts = " + accounts); 1876 1877 // Only dial with the requested phoneAccount if it is still valid. Otherwise treat this 1878 // call as if a phoneAccount was not specified (does the default behavior instead). 1879 // Note: We will not attempt to dial with a requested phoneAccount if it is disabled. 1880 if (targetPhoneAccountHandle != null) { 1881 if (accounts.contains(targetPhoneAccountHandle)) { 1882 // The target phone account is valid and was found. 1883 return CompletableFuture.completedFuture(Arrays.asList(targetPhoneAccountHandle)); 1884 } 1885 } 1886 if (accounts.isEmpty() || accounts.size() == 1) { 1887 return CompletableFuture.completedFuture(accounts); 1888 } 1889 1890 // Do the query for whether there's a preferred contact 1891 final CompletableFuture<PhoneAccountHandle> userPreferredAccountForContact = 1892 new CompletableFuture<>(); 1893 final List<PhoneAccountHandle> possibleAccounts = accounts; 1894 mCallerInfoLookupHelper.startLookup(handle, 1895 new CallerInfoLookupHelper.OnQueryCompleteListener() { 1896 @Override 1897 public void onCallerInfoQueryComplete(Uri handle, CallerInfo info) { 1898 if (info != null && 1899 info.preferredPhoneAccountComponent != null && 1900 info.preferredPhoneAccountId != null && 1901 !info.preferredPhoneAccountId.isEmpty()) { 1902 PhoneAccountHandle contactDefaultHandle = new PhoneAccountHandle( 1903 info.preferredPhoneAccountComponent, 1904 info.preferredPhoneAccountId, 1905 initiatingUser); 1906 userPreferredAccountForContact.complete(contactDefaultHandle); 1907 } else { 1908 userPreferredAccountForContact.complete(null); 1909 } 1910 } 1911 1912 @Override 1913 public void onContactPhotoQueryComplete(Uri handle, CallerInfo info) { 1914 // ignore this 1915 } 1916 }); 1917 1918 return userPreferredAccountForContact.thenApply(phoneAccountHandle -> { 1919 if (phoneAccountHandle != null) { 1920 return Collections.singletonList(phoneAccountHandle); 1921 } 1922 // No preset account, check if default exists that supports the URI scheme for the 1923 // handle and verify it can be used. 1924 PhoneAccountHandle defaultPhoneAccountHandle = 1925 mPhoneAccountRegistrar.getOutgoingPhoneAccountForScheme( 1926 handle.getScheme(), initiatingUser); 1927 if (defaultPhoneAccountHandle != null && 1928 possibleAccounts.contains(defaultPhoneAccountHandle)) { 1929 return Collections.singletonList(defaultPhoneAccountHandle); 1930 } 1931 return possibleAccounts; 1932 }); 1933 } 1934 1935 /** 1936 * Determines if a {@link PhoneAccountHandle} is for a self-managed ConnectionService. 1937 * @param targetPhoneAccountHandle The phone account to check. 1938 * @param initiatingUser The user associated with the account. 1939 * @return {@code true} if the phone account is self-managed, {@code false} otherwise. 1940 */ 1941 public boolean isSelfManaged(PhoneAccountHandle targetPhoneAccountHandle, 1942 UserHandle initiatingUser) { 1943 PhoneAccount targetPhoneAccount = mPhoneAccountRegistrar.getPhoneAccount( 1944 targetPhoneAccountHandle, initiatingUser); 1945 return targetPhoneAccount != null && targetPhoneAccount.isSelfManaged(); 1946 } 1947 1948 public void onCallRedirectionComplete(Call call, Uri handle, 1949 PhoneAccountHandle phoneAccountHandle, 1950 GatewayInfo gatewayInfo, boolean speakerphoneOn, 1951 int videoState, boolean shouldCancelCall, 1952 String uiAction) { 1953 Log.i(this, "onCallRedirectionComplete for Call %s with handle %s" + 1954 " and phoneAccountHandle %s", call, handle, phoneAccountHandle); 1955 1956 boolean endEarly = false; 1957 String disconnectReason = ""; 1958 String callRedirectionApp = mRoleManagerAdapter.getDefaultCallRedirectionApp(); 1959 1960 boolean isPotentialEmergencyNumber; 1961 try { 1962 isPotentialEmergencyNumber = 1963 handle != null && getTelephonyManager().isPotentialEmergencyNumber( 1964 handle.getSchemeSpecificPart()); 1965 } catch (IllegalStateException ise) { 1966 isPotentialEmergencyNumber = false; 1967 } 1968 1969 if (shouldCancelCall) { 1970 Log.w(this, "onCallRedirectionComplete: call is canceled"); 1971 endEarly = true; 1972 disconnectReason = "Canceled from Call Redirection Service"; 1973 1974 // Show UX when user-defined call redirection service does not response; the UX 1975 // is not needed to show if the call is disconnected (e.g. by the user) 1976 if (uiAction.equals(CallRedirectionProcessor.UI_TYPE_USER_DEFINED_TIMEOUT) 1977 && !call.isDisconnected()) { 1978 Intent timeoutIntent = new Intent(mContext, 1979 CallRedirectionTimeoutDialogActivity.class); 1980 timeoutIntent.putExtra( 1981 CallRedirectionTimeoutDialogActivity.EXTRA_REDIRECTION_APP_NAME, 1982 mRoleManagerAdapter.getApplicationLabelForPackageName(callRedirectionApp)); 1983 timeoutIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 1984 mContext.startActivityAsUser(timeoutIntent, UserHandle.CURRENT); 1985 } 1986 } else if (handle == null) { 1987 Log.w(this, "onCallRedirectionComplete: handle is null"); 1988 endEarly = true; 1989 disconnectReason = "Null handle from Call Redirection Service"; 1990 } else if (phoneAccountHandle == null) { 1991 Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is null"); 1992 endEarly = true; 1993 disconnectReason = "Null phoneAccountHandle from Call Redirection Service"; 1994 } else if (isPotentialEmergencyNumber) { 1995 Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call" 1996 + " Redirection Service", handle.getSchemeSpecificPart()); 1997 endEarly = true; 1998 disconnectReason = "Emergency number is redirected from Call Redirection Service"; 1999 } 2000 if (endEarly) { 2001 if (call != null) { 2002 call.disconnect(disconnectReason); 2003 } 2004 return; 2005 } 2006 2007 // If this call is already disconnected then we have nothing more to do. 2008 if (call.isDisconnected()) { 2009 Log.w(this, "onCallRedirectionComplete: Call has already been disconnected," 2010 + " ignore the call redirection %s", call); 2011 return; 2012 } 2013 2014 if (uiAction.equals(CallRedirectionProcessor.UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM)) { 2015 Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMATION); 2016 mPendingRedirectedOutgoingCall = call; 2017 2018 mPendingRedirectedOutgoingCallInfo.put(call.getId(), 2019 new Runnable("CM.oCRC", mLock) { 2020 @Override 2021 public void loggedRun() { 2022 Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMED); 2023 call.setTargetPhoneAccount(phoneAccountHandle); 2024 placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, 2025 videoState); 2026 } 2027 }); 2028 2029 mPendingUnredirectedOutgoingCallInfo.put(call.getId(), 2030 new Runnable("CM.oCRC", mLock) { 2031 @Override 2032 public void loggedRun() { 2033 call.setTargetPhoneAccount(phoneAccountHandle); 2034 placeOutgoingCall(call, handle, null, speakerphoneOn, 2035 videoState); 2036 } 2037 }); 2038 2039 Log.i(this, "onCallRedirectionComplete: UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM " 2040 + "callId=%s, callRedirectionAppName=%s", 2041 call.getId(), callRedirectionApp); 2042 2043 showRedirectionDialog(call.getId(), 2044 mRoleManagerAdapter.getApplicationLabelForPackageName(callRedirectionApp)); 2045 } else { 2046 call.setTargetPhoneAccount(phoneAccountHandle); 2047 placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn, videoState); 2048 } 2049 } 2050 2051 /** 2052 * Shows the call redirection confirmation dialog. This is explicitly done here instead of in 2053 * an activity class such as {@link ConfirmCallDialogActivity}. This was originally done with 2054 * an activity class, however due to the fact that the InCall UI is being spun up at the same 2055 * time as the dialog activity, there is a potential race condition where the InCall UI will 2056 * often be shown instead of the dialog. Activity manager chooses not to show the redirection 2057 * dialog in that case since the new top activity from dialer is going to show. 2058 * By showing the dialog here we're able to set the dialog's window type to 2059 * {@link WindowManager.LayoutParams#TYPE_SYSTEM_ALERT} which guarantees it shows above other 2060 * content on the screen. 2061 * @param callId The ID of the call to show the redirection dialog for. 2062 */ 2063 private void showRedirectionDialog(@NonNull String callId, @NonNull CharSequence appName) { 2064 AlertDialog confirmDialog = new AlertDialog.Builder(mContext).create(); 2065 LayoutInflater layoutInflater = LayoutInflater.from(mContext); 2066 View dialogView = layoutInflater.inflate(R.layout.call_redirection_confirm_dialog, null); 2067 2068 Button buttonFirstLine = (Button) dialogView.findViewById(R.id.buttonFirstLine); 2069 buttonFirstLine.setOnClickListener(new View.OnClickListener() { 2070 @Override 2071 public void onClick(View v) { 2072 Intent proceedWithoutRedirectedCall = new Intent( 2073 TelecomBroadcastIntentProcessor.ACTION_PLACE_UNREDIRECTED_CALL, 2074 null, mContext, 2075 TelecomBroadcastReceiver.class); 2076 proceedWithoutRedirectedCall.putExtra( 2077 TelecomBroadcastIntentProcessor.EXTRA_REDIRECTION_OUTGOING_CALL_ID, 2078 callId); 2079 mContext.sendBroadcast(proceedWithoutRedirectedCall); 2080 confirmDialog.dismiss(); 2081 } 2082 }); 2083 2084 Button buttonSecondLine = (Button) dialogView.findViewById(R.id.buttonSecondLine); 2085 buttonSecondLine.setText(mContext.getString( 2086 R.string.alert_place_outgoing_call_with_redirection, appName)); 2087 buttonSecondLine.setOnClickListener(new View.OnClickListener() { 2088 @Override 2089 public void onClick(View v) { 2090 Intent proceedWithRedirectedCall = new Intent( 2091 TelecomBroadcastIntentProcessor.ACTION_PLACE_REDIRECTED_CALL, null, 2092 mContext, 2093 TelecomBroadcastReceiver.class); 2094 proceedWithRedirectedCall.putExtra( 2095 TelecomBroadcastIntentProcessor.EXTRA_REDIRECTION_OUTGOING_CALL_ID, 2096 callId); 2097 mContext.sendBroadcast(proceedWithRedirectedCall); 2098 confirmDialog.dismiss(); 2099 } 2100 }); 2101 2102 Button buttonThirdLine = (Button) dialogView.findViewById(R.id.buttonThirdLine); 2103 buttonThirdLine.setOnClickListener(new View.OnClickListener() { 2104 public void onClick(View v) { 2105 cancelRedirection(callId); 2106 confirmDialog.dismiss(); 2107 } 2108 }); 2109 2110 confirmDialog.setOnCancelListener(new DialogInterface.OnCancelListener() { 2111 @Override 2112 public void onCancel(DialogInterface dialog) { 2113 cancelRedirection(callId); 2114 confirmDialog.dismiss(); 2115 } 2116 }); 2117 2118 confirmDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 2119 confirmDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT); 2120 2121 confirmDialog.setCancelable(false); 2122 confirmDialog.setCanceledOnTouchOutside(false); 2123 confirmDialog.setView(dialogView); 2124 2125 confirmDialog.show(); 2126 } 2127 2128 /** 2129 * Signals to Telecom that redirection of the call is to be cancelled. 2130 */ 2131 private void cancelRedirection(String callId) { 2132 Intent cancelRedirectedCall = new Intent( 2133 TelecomBroadcastIntentProcessor.ACTION_CANCEL_REDIRECTED_CALL, 2134 null, mContext, 2135 TelecomBroadcastReceiver.class); 2136 cancelRedirectedCall.putExtra( 2137 TelecomBroadcastIntentProcessor.EXTRA_REDIRECTION_OUTGOING_CALL_ID, callId); 2138 mContext.sendBroadcastAsUser(cancelRedirectedCall, UserHandle.CURRENT); 2139 } 2140 2141 public void processRedirectedOutgoingCallAfterUserInteraction(String callId, String action) { 2142 Log.i(this, "processRedirectedOutgoingCallAfterUserInteraction for Call ID %s, action=%s", 2143 callId, action); 2144 if (mPendingRedirectedOutgoingCall != null && mPendingRedirectedOutgoingCall.getId() 2145 .equals(callId)) { 2146 if (action.equals(TelecomBroadcastIntentProcessor.ACTION_PLACE_REDIRECTED_CALL)) { 2147 mHandler.post(mPendingRedirectedOutgoingCallInfo.get(callId).prepare()); 2148 } else if (action.equals( 2149 TelecomBroadcastIntentProcessor.ACTION_PLACE_UNREDIRECTED_CALL)) { 2150 mHandler.post(mPendingUnredirectedOutgoingCallInfo.get(callId).prepare()); 2151 } else if (action.equals( 2152 TelecomBroadcastIntentProcessor.ACTION_CANCEL_REDIRECTED_CALL)) { 2153 Log.addEvent(mPendingRedirectedOutgoingCall, 2154 LogUtils.Events.REDIRECTION_USER_CANCELLED); 2155 mPendingRedirectedOutgoingCall.disconnect("User canceled the redirected call."); 2156 } 2157 mPendingRedirectedOutgoingCall = null; 2158 mPendingRedirectedOutgoingCallInfo.remove(callId); 2159 mPendingUnredirectedOutgoingCallInfo.remove(callId); 2160 } else { 2161 Log.w(this, "processRedirectedOutgoingCallAfterUserInteraction for non-matched Call ID" 2162 + " %s", callId); 2163 } 2164 } 2165 2166 /** 2167 * Attempts to issue/connect the specified call. 2168 * 2169 * @param handle Handle to connect the call with. 2170 * @param gatewayInfo Optional gateway information that can be used to route the call to the 2171 * actual dialed handle via a gateway provider. May be null. 2172 * @param speakerphoneOn Whether or not to turn the speakerphone on once the call connects. 2173 * @param videoState The desired video state for the outgoing call. 2174 */ 2175 @VisibleForTesting 2176 public void placeOutgoingCall(Call call, Uri handle, GatewayInfo gatewayInfo, 2177 boolean speakerphoneOn, int videoState) { 2178 if (call == null) { 2179 // don't do anything if the call no longer exists 2180 Log.i(this, "Canceling unknown call."); 2181 return; 2182 } 2183 2184 final Uri uriHandle = (gatewayInfo == null) ? handle : gatewayInfo.getGatewayAddress(); 2185 2186 if (gatewayInfo == null) { 2187 Log.i(this, "Creating a new outgoing call with handle: %s", Log.piiHandle(uriHandle)); 2188 } else { 2189 Log.i(this, "Creating a new outgoing call with gateway handle: %s, original handle: %s", 2190 Log.pii(uriHandle), Log.pii(handle)); 2191 } 2192 2193 call.setHandle(uriHandle); 2194 call.setGatewayInfo(gatewayInfo); 2195 2196 final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean( 2197 R.bool.use_speaker_when_docked); 2198 final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock(); 2199 final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState); 2200 2201 // Auto-enable speakerphone if the originating intent specified to do so, if the call 2202 // is a video call, of if using speaker when docked 2203 PhoneAccount account = mPhoneAccountRegistrar.getPhoneAccount( 2204 call.getTargetPhoneAccount(), call.getInitiatingUser()); 2205 boolean allowVideo = false; 2206 if (account != null) { 2207 allowVideo = account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING); 2208 } 2209 call.setStartWithSpeakerphoneOn(speakerphoneOn || (useSpeakerForVideoCall && allowVideo) 2210 || (useSpeakerWhenDocked && useSpeakerForDock)); 2211 call.setVideoState(videoState); 2212 2213 if (speakerphoneOn) { 2214 Log.i(this, "%s Starting with speakerphone as requested", call); 2215 } else if (useSpeakerWhenDocked && useSpeakerForDock) { 2216 Log.i(this, "%s Starting with speakerphone because car is docked.", call); 2217 } else if (useSpeakerForVideoCall) { 2218 Log.i(this, "%s Starting with speakerphone because its a video call.", call); 2219 } 2220 2221 if (call.isEmergencyCall()) { 2222 Executors.defaultThreadFactory().newThread(() -> 2223 BlockedNumberContract.SystemContract.notifyEmergencyContact(mContext)) 2224 .start(); 2225 } 2226 2227 final boolean requireCallCapableAccountByHandle = mContext.getResources().getBoolean( 2228 com.android.internal.R.bool.config_requireCallCapableAccountForHandle); 2229 final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call, 2230 call.getTargetPhoneAccount()); 2231 final String callHandleScheme = 2232 call.getHandle() == null ? null : call.getHandle().getScheme(); 2233 if (call.getTargetPhoneAccount() != null || call.isEmergencyCall()) { 2234 // If the account has been set, proceed to place the outgoing call. 2235 // Otherwise the connection will be initiated when the account is set by the user. 2236 if (call.isSelfManaged() && !isOutgoingCallPermitted) { 2237 if (call.isAdhocConferenceCall()) { 2238 notifyCreateConferenceFailed(call.getTargetPhoneAccount(), call); 2239 } else { 2240 notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call); 2241 } 2242 } else { 2243 if (call.isEmergencyCall()) { 2244 // Drop any ongoing self-managed calls to make way for an emergency call. 2245 disconnectSelfManagedCalls("place emerg call" /* reason */); 2246 } 2247 2248 call.startCreateConnection(mPhoneAccountRegistrar); 2249 } 2250 } else if (mPhoneAccountRegistrar.getCallCapablePhoneAccounts( 2251 requireCallCapableAccountByHandle ? callHandleScheme : null, false, 2252 call.getInitiatingUser()).isEmpty()) { 2253 // If there are no call capable accounts, disconnect the call. 2254 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.CANCELED, 2255 "No registered PhoneAccounts")); 2256 markCallAsRemoved(call); 2257 } 2258 } 2259 2260 /** 2261 * Attempts to start a conference call for the specified call. 2262 * 2263 * @param call The call to conference. 2264 * @param otherCall The other call to conference with. 2265 */ 2266 @VisibleForTesting 2267 public void conference(Call call, Call otherCall) { 2268 call.conferenceWith(otherCall); 2269 } 2270 2271 /** 2272 * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call 2273 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 2274 * the user opting to answer said call. 2275 * 2276 * @param call The call to answer. 2277 * @param videoState The video state in which to answer the call. 2278 */ 2279 @VisibleForTesting 2280 public void answerCall(Call call, int videoState) { 2281 if (!mCalls.contains(call)) { 2282 Log.i(this, "Request to answer a non-existent call %s", call); 2283 } else { 2284 // Hold or disconnect the active call and request call focus for the incoming call. 2285 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); 2286 Log.d(this, "answerCall: Incoming call = %s Ongoing call %s", call, activeCall); 2287 holdActiveCallForNewCall(call); 2288 mConnectionSvrFocusMgr.requestFocus( 2289 call, 2290 new RequestCallback(new ActionAnswerCall(call, videoState))); 2291 } 2292 } 2293 2294 private void answerCallForAudioProcessing(Call call) { 2295 // We don't check whether the call has been added to the internal lists yet -- it's optional 2296 // until the call is actually in the AUDIO_PROCESSING state. 2297 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); 2298 if (activeCall != null && activeCall != call) { 2299 Log.w(this, "answerCallForAudioProcessing: another active call already exists. " 2300 + "Ignoring request for audio processing and letting the incoming call " 2301 + "through."); 2302 // The call should already be in the RINGING state, so all we have to do is add the 2303 // call to the internal tracker. 2304 addCall(call); 2305 return; 2306 } 2307 Log.d(this, "answerCallForAudioProcessing: Incoming call = %s", call); 2308 mConnectionSvrFocusMgr.requestFocus( 2309 call, 2310 new RequestCallback(() -> { 2311 synchronized (mLock) { 2312 Log.d(this, "answering call %s for audio processing with cs focus", call); 2313 call.answerForAudioProcessing(); 2314 // Skip setting the call state to ANSWERED -- that's only for calls that 2315 // were answered by user intervention. 2316 mPendingAudioProcessingCall = call; 2317 } 2318 })); 2319 2320 } 2321 2322 /** 2323 * Instructs Telecom to bring a call into the AUDIO_PROCESSING state. 2324 * 2325 * Used by the background audio call screener (also the default dialer) to signal that 2326 * they want to manually enter the AUDIO_PROCESSING state. The user will be aware that there is 2327 * an ongoing call at this time. 2328 * 2329 * @param call The call to manipulate 2330 */ 2331 public void enterBackgroundAudioProcessing(Call call, String requestingPackageName) { 2332 if (!mCalls.contains(call)) { 2333 Log.w(this, "Trying to exit audio processing on an untracked call"); 2334 return; 2335 } 2336 2337 Call activeCall = getActiveCall(); 2338 if (activeCall != null && activeCall != call) { 2339 Log.w(this, "Ignoring enter audio processing because there's already a call active"); 2340 return; 2341 } 2342 2343 CharSequence requestingAppName = AppLabelProxy.Util.getAppLabel( 2344 mContext.getPackageManager(), requestingPackageName); 2345 if (requestingAppName == null) { 2346 requestingAppName = requestingPackageName; 2347 } 2348 2349 // We only want this to work on active or ringing calls 2350 if (call.getState() == CallState.RINGING) { 2351 // After the connection service sets up the call with the other end, it'll set the call 2352 // state to AUDIO_PROCESSING 2353 answerCallForAudioProcessing(call); 2354 call.setAudioProcessingRequestingApp(requestingAppName); 2355 } else if (call.getState() == CallState.ACTIVE) { 2356 setCallState(call, CallState.AUDIO_PROCESSING, 2357 "audio processing set by dialer request"); 2358 call.setAudioProcessingRequestingApp(requestingAppName); 2359 } 2360 } 2361 2362 /** 2363 * Instructs Telecom to bring a call out of the AUDIO_PROCESSING state. 2364 * 2365 * Used by the background audio call screener (also the default dialer) to signal that it's 2366 * finished doing its thing and the user should be made aware of the call. 2367 * 2368 * @param call The call to manipulate 2369 * @param shouldRing if true, puts the call into SIMULATED_RINGING. Otherwise, makes the call 2370 * active. 2371 */ 2372 public void exitBackgroundAudioProcessing(Call call, boolean shouldRing) { 2373 if (!mCalls.contains(call)) { 2374 Log.w(this, "Trying to exit audio processing on an untracked call"); 2375 return; 2376 } 2377 2378 Call activeCall = getActiveCall(); 2379 if (activeCall != null) { 2380 Log.w(this, "Ignoring exit audio processing because there's already a call active"); 2381 } 2382 2383 if (shouldRing) { 2384 setCallState(call, CallState.SIMULATED_RINGING, "exitBackgroundAudioProcessing"); 2385 } else { 2386 setCallState(call, CallState.ACTIVE, "exitBackgroundAudioProcessing"); 2387 } 2388 } 2389 2390 /** 2391 * Instructs Telecom to deflect the specified call. Intended to be invoked by the in-call 2392 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 2393 * the user opting to deflect said call. 2394 */ 2395 @VisibleForTesting 2396 public void deflectCall(Call call, Uri address) { 2397 if (!mCalls.contains(call)) { 2398 Log.i(this, "Request to deflect a non-existent call %s", call); 2399 } else { 2400 call.deflect(address); 2401 } 2402 } 2403 2404 /** 2405 * Determines if the speakerphone should be automatically enabled for the call. Speakerphone 2406 * should be enabled if the call is a video call and bluetooth or the wired headset are not in 2407 * use. 2408 * 2409 * @param videoState The video state of the call. 2410 * @return {@code true} if the speakerphone should be enabled. 2411 */ 2412 public boolean isSpeakerphoneAutoEnabledForVideoCalls(int videoState) { 2413 return VideoProfile.isVideo(videoState) && 2414 !mWiredHeadsetManager.isPluggedIn() && 2415 !mBluetoothRouteManager.isBluetoothAvailable() && 2416 isSpeakerEnabledForVideoCalls(); 2417 } 2418 2419 /** 2420 * Determines if the speakerphone should be enabled for when docked. Speakerphone 2421 * should be enabled if the device is docked and bluetooth or the wired headset are 2422 * not in use. 2423 * 2424 * @return {@code true} if the speakerphone should be enabled for the dock. 2425 */ 2426 private boolean isSpeakerphoneEnabledForDock() { 2427 return mDockManager.isDocked() && 2428 !mWiredHeadsetManager.isPluggedIn() && 2429 !mBluetoothRouteManager.isBluetoothAvailable(); 2430 } 2431 2432 /** 2433 * Determines if the speakerphone should be automatically enabled for video calls. 2434 * 2435 * @return {@code true} if the speakerphone should automatically be enabled. 2436 */ 2437 private static boolean isSpeakerEnabledForVideoCalls() { 2438 return TelephonyProperties.videocall_audio_output() 2439 .orElse(TelecomManager.AUDIO_OUTPUT_DEFAULT) 2440 == TelecomManager.AUDIO_OUTPUT_ENABLE_SPEAKER; 2441 } 2442 2443 /** 2444 * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call 2445 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 2446 * the user opting to reject said call. 2447 */ 2448 @VisibleForTesting 2449 public void rejectCall(Call call, boolean rejectWithMessage, String textMessage) { 2450 if (!mCalls.contains(call)) { 2451 Log.i(this, "Request to reject a non-existent call %s", call); 2452 } else { 2453 for (CallsManagerListener listener : mListeners) { 2454 listener.onIncomingCallRejected(call, rejectWithMessage, textMessage); 2455 } 2456 call.reject(rejectWithMessage, textMessage); 2457 } 2458 } 2459 2460 /** 2461 * Instructs Telecom to reject the specified call. Intended to be invoked by the in-call 2462 * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by 2463 * the user opting to reject said call. 2464 */ 2465 @VisibleForTesting 2466 public void rejectCall(Call call, @android.telecom.Call.RejectReason int rejectReason) { 2467 if (!mCalls.contains(call)) { 2468 Log.i(this, "Request to reject a non-existent call %s", call); 2469 } else { 2470 for (CallsManagerListener listener : mListeners) { 2471 listener.onIncomingCallRejected(call, false /* rejectWithMessage */, 2472 null /* textMessage */); 2473 } 2474 call.reject(rejectReason); 2475 } 2476 } 2477 2478 /** 2479 * Instructs Telecom to transfer the specified call. Intended to be invoked by the in-call 2480 * app through {@link InCallAdapter} after the user opts to transfer the said call. 2481 */ 2482 @VisibleForTesting 2483 public void transferCall(Call call, Uri number, boolean isConfirmationRequired) { 2484 if (!mCalls.contains(call)) { 2485 Log.i(this, "transferCall - Request to transfer a non-existent call %s", call); 2486 } else { 2487 call.transfer(number, isConfirmationRequired); 2488 } 2489 } 2490 2491 /** 2492 * Instructs Telecom to transfer the specified call to another ongoing call. 2493 * Intended to be invoked by the in-call app through {@link InCallAdapter} after the user opts 2494 * to transfer the said call (consultative transfer). 2495 */ 2496 @VisibleForTesting 2497 public void transferCall(Call call, Call otherCall) { 2498 if (!mCalls.contains(call) || !mCalls.contains(otherCall)) { 2499 Log.i(this, "transferCall - Non-existent call %s or %s", call, otherCall); 2500 } else { 2501 call.transfer(otherCall); 2502 } 2503 } 2504 2505 /** 2506 * Instructs Telecom to play the specified DTMF tone within the specified call. 2507 * 2508 * @param digit The DTMF digit to play. 2509 */ 2510 @VisibleForTesting 2511 public void playDtmfTone(Call call, char digit) { 2512 if (!mCalls.contains(call)) { 2513 Log.i(this, "Request to play DTMF in a non-existent call %s", call); 2514 } else { 2515 if (call.getState() != CallState.ON_HOLD) { 2516 call.playDtmfTone(digit); 2517 mDtmfLocalTonePlayer.playTone(call, digit); 2518 } else { 2519 Log.i(this, "Request to play DTMF tone for held call %s", call.getId()); 2520 } 2521 } 2522 } 2523 2524 /** 2525 * Instructs Telecom to stop the currently playing DTMF tone, if any. 2526 */ 2527 @VisibleForTesting 2528 public void stopDtmfTone(Call call) { 2529 if (!mCalls.contains(call)) { 2530 Log.i(this, "Request to stop DTMF in a non-existent call %s", call); 2531 } else { 2532 call.stopDtmfTone(); 2533 mDtmfLocalTonePlayer.stopTone(call); 2534 } 2535 } 2536 2537 /** 2538 * Instructs Telecom to continue (or not) the current post-dial DTMF string, if any. 2539 */ 2540 void postDialContinue(Call call, boolean proceed) { 2541 if (!mCalls.contains(call)) { 2542 Log.i(this, "Request to continue post-dial string in a non-existent call %s", call); 2543 } else { 2544 call.postDialContinue(proceed); 2545 } 2546 } 2547 2548 /** 2549 * Instructs Telecom to disconnect the specified call. Intended to be invoked by the 2550 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 2551 * the user hitting the end-call button. 2552 */ 2553 @VisibleForTesting 2554 public void disconnectCall(Call call) { 2555 Log.v(this, "disconnectCall %s", call); 2556 2557 if (!mCalls.contains(call)) { 2558 Log.w(this, "Unknown call (%s) asked to disconnect", call); 2559 } else { 2560 mLocallyDisconnectingCalls.add(call); 2561 int previousState = call.getState(); 2562 call.disconnect(); 2563 for (CallsManagerListener listener : mListeners) { 2564 listener.onCallStateChanged(call, previousState, call.getState()); 2565 } 2566 // Cancel any of the outgoing call futures if they're still around. 2567 if (mPendingCallConfirm != null && !mPendingCallConfirm.isDone()) { 2568 mPendingCallConfirm.complete(null); 2569 mPendingCallConfirm = null; 2570 } 2571 if (mPendingAccountSelection != null && !mPendingAccountSelection.isDone()) { 2572 mPendingAccountSelection.complete(null); 2573 mPendingAccountSelection = null; 2574 } 2575 } 2576 } 2577 2578 /** 2579 * Instructs Telecom to disconnect all calls. 2580 */ 2581 void disconnectAllCalls() { 2582 Log.v(this, "disconnectAllCalls"); 2583 2584 for (Call call : mCalls) { 2585 disconnectCall(call); 2586 } 2587 } 2588 2589 /** 2590 * Disconnects calls for any other {@link PhoneAccountHandle} but the one specified. 2591 * Note: As a protective measure, will NEVER disconnect an emergency call. Although that 2592 * situation should never arise, its a good safeguard. 2593 * @param phoneAccountHandle Calls owned by {@link PhoneAccountHandle}s other than this one will 2594 * be disconnected. 2595 */ 2596 private void disconnectOtherCalls(PhoneAccountHandle phoneAccountHandle) { 2597 mCalls.stream() 2598 .filter(c -> !c.isEmergencyCall() && 2599 !c.getTargetPhoneAccount().equals(phoneAccountHandle)) 2600 .forEach(c -> disconnectCall(c)); 2601 } 2602 2603 /** 2604 * Instructs Telecom to put the specified call on hold. Intended to be invoked by the 2605 * in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered by 2606 * the user hitting the hold button during an active call. 2607 */ 2608 @VisibleForTesting 2609 public void holdCall(Call call) { 2610 if (!mCalls.contains(call)) { 2611 Log.w(this, "Unknown call (%s) asked to be put on hold", call); 2612 } else { 2613 Log.d(this, "Putting call on hold: (%s)", call); 2614 call.hold(); 2615 } 2616 } 2617 2618 /** 2619 * Instructs Telecom to release the specified call from hold. Intended to be invoked by 2620 * the in-call app through {@link InCallAdapter} for an ongoing call. This is usually triggered 2621 * by the user hitting the hold button during a held call. 2622 */ 2623 @VisibleForTesting 2624 public void unholdCall(Call call) { 2625 if (!mCalls.contains(call)) { 2626 Log.w(this, "Unknown call (%s) asked to be removed from hold", call); 2627 } else { 2628 if (getOutgoingCall() != null) { 2629 Log.w(this, "There is an outgoing call, so it is unable to unhold this call %s", 2630 call); 2631 return; 2632 } 2633 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); 2634 String activeCallId = null; 2635 if (activeCall != null && !activeCall.isLocallyDisconnecting()) { 2636 activeCallId = activeCall.getId(); 2637 if (canHold(activeCall)) { 2638 activeCall.hold("Swap to " + call.getId()); 2639 Log.addEvent(activeCall, LogUtils.Events.SWAP, "To " + call.getId()); 2640 Log.addEvent(call, LogUtils.Events.SWAP, "From " + activeCall.getId()); 2641 } else { 2642 // This call does not support hold. If it is from a different connection 2643 // service or connection manager, then disconnect it, otherwise invoke 2644 // call.hold() and allow the connection service or connection manager to handle 2645 // the situation. 2646 if (!areFromSameSource(activeCall, call)) { 2647 if (!activeCall.isEmergencyCall()) { 2648 activeCall.disconnect("Swap to " + call.getId()); 2649 } else { 2650 Log.w(this, "unholdCall: % is an emergency call, aborting swap to %s", 2651 activeCall.getId(), call.getId()); 2652 // Don't unhold the call as requested; we don't want to drop an 2653 // emergency call. 2654 return; 2655 } 2656 } else { 2657 activeCall.hold("Swap to " + call.getId()); 2658 } 2659 } 2660 } 2661 mConnectionSvrFocusMgr.requestFocus( 2662 call, 2663 new RequestCallback(new ActionUnHoldCall(call, activeCallId))); 2664 } 2665 } 2666 2667 @Override 2668 public void onExtrasRemoved(Call c, int source, List<String> keys) { 2669 if (source != Call.SOURCE_CONNECTION_SERVICE) { 2670 return; 2671 } 2672 updateCanAddCall(); 2673 } 2674 2675 @Override 2676 public void onExtrasChanged(Call c, int source, Bundle extras) { 2677 if (source != Call.SOURCE_CONNECTION_SERVICE) { 2678 return; 2679 } 2680 handleCallTechnologyChange(c); 2681 handleChildAddressChange(c); 2682 updateCanAddCall(); 2683 } 2684 2685 // Construct the list of possible PhoneAccounts that the outgoing call can use based on the 2686 // active calls in CallsManager. If any of the active calls are on a SIM based PhoneAccount, 2687 // then include only that SIM based PhoneAccount and any non-SIM PhoneAccounts, such as SIP. 2688 @VisibleForTesting 2689 public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user, 2690 boolean isVideo, boolean isEmergency) { 2691 return constructPossiblePhoneAccounts(handle, user, isVideo, isEmergency, false); 2692 } 2693 2694 public List<PhoneAccountHandle> constructPossiblePhoneAccounts(Uri handle, UserHandle user, 2695 boolean isVideo, boolean isEmergency, boolean isConference) { 2696 2697 if (handle == null) { 2698 return Collections.emptyList(); 2699 } 2700 // If we're specifically looking for video capable accounts, then include that capability, 2701 // otherwise specify no additional capability constraints. When handling the emergency call, 2702 // it also needs to find the phone accounts excluded by CAPABILITY_EMERGENCY_CALLS_ONLY. 2703 int capabilities = isVideo ? PhoneAccount.CAPABILITY_VIDEO_CALLING : 0; 2704 capabilities |= isConference ? PhoneAccount.CAPABILITY_ADHOC_CONFERENCE_CALLING : 0; 2705 List<PhoneAccountHandle> allAccounts = 2706 mPhoneAccountRegistrar.getCallCapablePhoneAccounts(handle.getScheme(), false, user, 2707 capabilities, 2708 isEmergency ? 0 : PhoneAccount.CAPABILITY_EMERGENCY_CALLS_ONLY); 2709 if (mMaxNumberOfSimultaneouslyActiveSims < 0) { 2710 mMaxNumberOfSimultaneouslyActiveSims = 2711 getTelephonyManager().getMaxNumberOfSimultaneouslyActiveSims(); 2712 } 2713 // Only one SIM PhoneAccount can be active at one time for DSDS. Only that SIM PhoneAccount 2714 // should be available if a call is already active on the SIM account. 2715 if (mMaxNumberOfSimultaneouslyActiveSims == 1) { 2716 List<PhoneAccountHandle> simAccounts = 2717 mPhoneAccountRegistrar.getSimPhoneAccountsOfCurrentUser(); 2718 PhoneAccountHandle ongoingCallAccount = null; 2719 for (Call c : mCalls) { 2720 if (!c.isDisconnected() && !c.isNew() && simAccounts.contains( 2721 c.getTargetPhoneAccount())) { 2722 ongoingCallAccount = c.getTargetPhoneAccount(); 2723 break; 2724 } 2725 } 2726 if (ongoingCallAccount != null) { 2727 // Remove all SIM accounts that are not the active SIM from the list. 2728 simAccounts.remove(ongoingCallAccount); 2729 allAccounts.removeAll(simAccounts); 2730 } 2731 } 2732 return allAccounts; 2733 } 2734 2735 private TelephonyManager getTelephonyManager() { 2736 return mContext.getSystemService(TelephonyManager.class); 2737 } 2738 2739 /** 2740 * Informs listeners (notably {@link CallAudioManager} of a change to the call's external 2741 * property. 2742 * . 2743 * @param call The call whose external property changed. 2744 * @param isExternalCall {@code True} if the call is now external, {@code false} otherwise. 2745 */ 2746 @Override 2747 public void onExternalCallChanged(Call call, boolean isExternalCall) { 2748 Log.v(this, "onConnectionPropertiesChanged: %b", isExternalCall); 2749 for (CallsManagerListener listener : mListeners) { 2750 listener.onExternalCallChanged(call, isExternalCall); 2751 } 2752 } 2753 2754 private void handleCallTechnologyChange(Call call) { 2755 if (call.getExtras() != null 2756 && call.getExtras().containsKey(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE)) { 2757 2758 Integer analyticsCallTechnology = sAnalyticsTechnologyMap.get( 2759 call.getExtras().getInt(TelecomManager.EXTRA_CALL_TECHNOLOGY_TYPE)); 2760 if (analyticsCallTechnology == null) { 2761 analyticsCallTechnology = Analytics.THIRD_PARTY_PHONE; 2762 } 2763 call.getAnalytics().addCallTechnology(analyticsCallTechnology); 2764 } 2765 } 2766 2767 public void handleChildAddressChange(Call call) { 2768 if (call.getExtras() != null 2769 && call.getExtras().containsKey(Connection.EXTRA_CHILD_ADDRESS)) { 2770 2771 String viaNumber = call.getExtras().getString(Connection.EXTRA_CHILD_ADDRESS); 2772 call.setViaNumber(viaNumber); 2773 } 2774 } 2775 2776 /** Called by the in-call UI to change the mute state. */ 2777 void mute(boolean shouldMute) { 2778 if (isInEmergencyCall() && shouldMute) { 2779 Log.i(this, "Refusing to turn on mute because we're in an emergency call"); 2780 shouldMute = false; 2781 } 2782 mCallAudioManager.mute(shouldMute); 2783 } 2784 2785 /** 2786 * Called by the in-call UI to change the audio route, for example to change from earpiece to 2787 * speaker phone. 2788 */ 2789 void setAudioRoute(int route, String bluetoothAddress) { 2790 mCallAudioManager.setAudioRoute(route, bluetoothAddress); 2791 } 2792 2793 /** Called by the in-call UI to turn the proximity sensor on. */ 2794 void turnOnProximitySensor() { 2795 mProximitySensorManager.turnOn(); 2796 } 2797 2798 /** 2799 * Called by the in-call UI to turn the proximity sensor off. 2800 * @param screenOnImmediately If true, the screen will be turned on immediately. Otherwise, 2801 * the screen will be kept off until the proximity sensor goes negative. 2802 */ 2803 void turnOffProximitySensor(boolean screenOnImmediately) { 2804 mProximitySensorManager.turnOff(screenOnImmediately); 2805 } 2806 2807 private boolean isRttSettingOn(PhoneAccountHandle handle) { 2808 boolean isRttModeSettingOn = Settings.Secure.getInt(mContext.getContentResolver(), 2809 Settings.Secure.RTT_CALLING_MODE, 0) != 0; 2810 // If the carrier config says that we should ignore the RTT mode setting from the user, 2811 // assume that it's off (i.e. only make an RTT call if it's requested through the extra). 2812 boolean shouldIgnoreRttModeSetting = getCarrierConfigForPhoneAccount(handle) 2813 .getBoolean(CarrierConfigManager.KEY_IGNORE_RTT_MODE_SETTING_BOOL, false); 2814 return isRttModeSettingOn && !shouldIgnoreRttModeSetting; 2815 } 2816 2817 private PersistableBundle getCarrierConfigForPhoneAccount(PhoneAccountHandle handle) { 2818 int subscriptionId = mPhoneAccountRegistrar.getSubscriptionIdForPhoneAccount(handle); 2819 CarrierConfigManager carrierConfigManager = 2820 mContext.getSystemService(CarrierConfigManager.class); 2821 PersistableBundle result = carrierConfigManager.getConfigForSubId(subscriptionId); 2822 return result == null ? new PersistableBundle() : result; 2823 } 2824 2825 void phoneAccountSelected(Call call, PhoneAccountHandle account, boolean setDefault) { 2826 if (!mCalls.contains(call)) { 2827 Log.i(this, "Attempted to add account to unknown call %s", call); 2828 } else { 2829 if (setDefault) { 2830 mPhoneAccountRegistrar 2831 .setUserSelectedOutgoingPhoneAccount(account, call.getInitiatingUser()); 2832 } 2833 2834 if (mPendingAccountSelection != null) { 2835 mPendingAccountSelection.complete(Pair.create(call, account)); 2836 mPendingAccountSelection = null; 2837 } 2838 } 2839 } 2840 2841 /** Called when the audio state changes. */ 2842 @VisibleForTesting 2843 public void onCallAudioStateChanged(CallAudioState oldAudioState, CallAudioState 2844 newAudioState) { 2845 Log.v(this, "onAudioStateChanged, audioState: %s -> %s", oldAudioState, newAudioState); 2846 for (CallsManagerListener listener : mListeners) { 2847 listener.onCallAudioStateChanged(oldAudioState, newAudioState); 2848 } 2849 } 2850 2851 /** 2852 * Called when disconnect tone is started or stopped, including any InCallTone 2853 * after disconnected call. 2854 * 2855 * @param isTonePlaying true if the disconnected tone is started, otherwise the disconnected 2856 * tone is stopped. 2857 */ 2858 @VisibleForTesting 2859 public void onDisconnectedTonePlaying(boolean isTonePlaying) { 2860 Log.v(this, "onDisconnectedTonePlaying, %s", isTonePlaying ? "started" : "stopped"); 2861 for (CallsManagerListener listener : mListeners) { 2862 listener.onDisconnectedTonePlaying(isTonePlaying); 2863 } 2864 } 2865 2866 void markCallAsRinging(Call call) { 2867 setCallState(call, CallState.RINGING, "ringing set explicitly"); 2868 } 2869 2870 void markCallAsDialing(Call call) { 2871 setCallState(call, CallState.DIALING, "dialing set explicitly"); 2872 maybeMoveToSpeakerPhone(call); 2873 maybeTurnOffMute(call); 2874 ensureCallAudible(); 2875 } 2876 2877 void markCallAsPulling(Call call) { 2878 setCallState(call, CallState.PULLING, "pulling set explicitly"); 2879 maybeMoveToSpeakerPhone(call); 2880 } 2881 2882 /** 2883 * Returns true if the active call is held. 2884 */ 2885 boolean holdActiveCallForNewCall(Call call) { 2886 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); 2887 if (activeCall != null && activeCall != call) { 2888 if (canHold(activeCall)) { 2889 activeCall.hold(); 2890 return true; 2891 } else if (supportsHold(activeCall) 2892 && areFromSameSource(activeCall, call)) { 2893 2894 // Handle the case where the active call and the new call are from the same CS or 2895 // connection manager, and the currently active call supports hold but cannot 2896 // currently be held. 2897 // In this case we'll look for the other held call for this connectionService and 2898 // disconnect it prior to holding the active call. 2899 // E.g. 2900 // Call A - Held (Supports hold, can't hold) 2901 // Call B - Active (Supports hold, can't hold) 2902 // Call C - Incoming 2903 // Here we need to disconnect A prior to holding B so that C can be answered. 2904 // This case is driven by telephony requirements ultimately. 2905 Call heldCall = getHeldCallByConnectionService(call.getTargetPhoneAccount()); 2906 if (heldCall != null) { 2907 heldCall.disconnect(); 2908 Log.i(this, "holdActiveCallForNewCall: Disconnect held call %s before " 2909 + "holding active call %s.", 2910 heldCall.getId(), activeCall.getId()); 2911 } 2912 Log.i(this, "holdActiveCallForNewCall: Holding active %s before making %s active.", 2913 activeCall.getId(), call.getId()); 2914 activeCall.hold(); 2915 return true; 2916 } else { 2917 // This call does not support hold. If it is from a different connection 2918 // service or connection manager, then disconnect it, otherwise allow the connection 2919 // service or connection manager to figure out the right states. 2920 if (!areFromSameSource(activeCall, call)) { 2921 Log.i(this, "holdActiveCallForNewCall: disconnecting %s so that %s can be " 2922 + "made active.", activeCall.getId(), call.getId()); 2923 if (!activeCall.isEmergencyCall()) { 2924 activeCall.disconnect(); 2925 } else { 2926 // It's not possible to hold the active call, and its an emergency call so 2927 // we will silently reject the incoming call instead of answering it. 2928 Log.w(this, "holdActiveCallForNewCall: rejecting incoming call %s as " 2929 + "the active call is an emergency call and it cannot be held.", 2930 call.getId()); 2931 call.reject(false /* rejectWithMessage */, "" /* message */, 2932 "active emergency call can't be held"); 2933 } 2934 } 2935 } 2936 } 2937 return false; 2938 } 2939 2940 @VisibleForTesting 2941 public void markCallAsActive(Call call) { 2942 if (call.isSelfManaged()) { 2943 // backward compatibility, the self-managed connection service will set the call state 2944 // to active directly. We should hold or disconnect the current active call based on the 2945 // holdability, and request the call focus for the self-managed call before the state 2946 // change. 2947 holdActiveCallForNewCall(call); 2948 mConnectionSvrFocusMgr.requestFocus( 2949 call, 2950 new RequestCallback(new ActionSetCallState( 2951 call, 2952 CallState.ACTIVE, 2953 "active set explicitly for self-managed"))); 2954 } else { 2955 if (mPendingAudioProcessingCall == call) { 2956 if (mCalls.contains(call)) { 2957 setCallState(call, CallState.AUDIO_PROCESSING, "active set explicitly"); 2958 } else { 2959 call.setState(CallState.AUDIO_PROCESSING, "active set explicitly and adding"); 2960 addCall(call); 2961 } 2962 // Clear mPendingAudioProcessingCall so that future attempts to mark the call as 2963 // active (e.g. coming off of hold) don't put the call into audio processing instead 2964 mPendingAudioProcessingCall = null; 2965 return; 2966 } 2967 setCallState(call, CallState.ACTIVE, "active set explicitly"); 2968 maybeMoveToSpeakerPhone(call); 2969 ensureCallAudible(); 2970 } 2971 } 2972 2973 @VisibleForTesting 2974 public void markCallAsOnHold(Call call) { 2975 setCallState(call, CallState.ON_HOLD, "on-hold set explicitly"); 2976 } 2977 2978 /** 2979 * Marks the specified call as STATE_DISCONNECTED and notifies the in-call app. If this was the 2980 * last live call, then also disconnect from the in-call controller. 2981 * 2982 * @param disconnectCause The disconnect cause, see {@link android.telecom.DisconnectCause}. 2983 */ 2984 void markCallAsDisconnected(Call call, DisconnectCause disconnectCause) { 2985 int oldState = call.getState(); 2986 if (call.getState() == CallState.SIMULATED_RINGING 2987 && disconnectCause.getCode() == DisconnectCause.REMOTE) { 2988 // If the remote end hangs up while in SIMULATED_RINGING, the call should 2989 // be marked as missed. 2990 call.setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.MISSED)); 2991 } 2992 call.setDisconnectCause(disconnectCause); 2993 setCallState(call, CallState.DISCONNECTED, "disconnected set explicitly"); 2994 2995 if(oldState == CallState.NEW && disconnectCause.getCode() == DisconnectCause.MISSED) { 2996 Log.i(this, "markCallAsDisconnected: logging missed call "); 2997 mCallLogManager.logCall(call, Calls.MISSED_TYPE, true, null); 2998 } 2999 3000 } 3001 3002 /** 3003 * Removes an existing disconnected call, and notifies the in-call app. 3004 */ 3005 void markCallAsRemoved(Call call) { 3006 mInCallController.getBindingFuture().thenRunAsync(() -> { 3007 call.maybeCleanupHandover(); 3008 removeCall(call); 3009 Call foregroundCall = mCallAudioManager.getPossiblyHeldForegroundCall(); 3010 if (mLocallyDisconnectingCalls.contains(call)) { 3011 boolean isDisconnectingChildCall = call.isDisconnectingChildCall(); 3012 Log.v(this, "markCallAsRemoved: isDisconnectingChildCall = " 3013 + isDisconnectingChildCall + "call -> %s", call); 3014 mLocallyDisconnectingCalls.remove(call); 3015 // Auto-unhold the foreground call due to a locally disconnected call, except if the 3016 // call which was disconnected is a member of a conference (don't want to auto 3017 // un-hold the conference if we remove a member of the conference). 3018 if (!isDisconnectingChildCall && foregroundCall != null 3019 && foregroundCall.getState() == CallState.ON_HOLD) { 3020 foregroundCall.unhold(); 3021 } 3022 } else if (foregroundCall != null && 3023 !foregroundCall.can(Connection.CAPABILITY_SUPPORT_HOLD) && 3024 foregroundCall.getState() == CallState.ON_HOLD) { 3025 3026 // The new foreground call is on hold, however the carrier does not display the hold 3027 // button in the UI. Therefore, we need to auto unhold the held call since the user 3028 // has no means of unholding it themselves. 3029 Log.i(this, "Auto-unholding held foreground call (call doesn't support hold)"); 3030 foregroundCall.unhold(); 3031 } 3032 }, new LoggedHandlerExecutor(mHandler, "CM.mCAR", mLock)); 3033 } 3034 3035 /** 3036 * Given a call, marks the call as disconnected and removes it. Set the error message to 3037 * indicate to the user that the call cannot me placed due to an ongoing call in another app. 3038 * 3039 * Used when there are ongoing self-managed calls and the user tries to make an outgoing managed 3040 * call. Called by {@link #startCallConfirmation} when the user is already confirming an 3041 * outgoing call. Realistically this should almost never be called since in practice the user 3042 * won't make multiple outgoing calls at the same time. 3043 * 3044 * @param call The call to mark as disconnected. 3045 */ 3046 void markCallDisconnectedDueToSelfManagedCall(Call call) { 3047 Call activeCall = getActiveCall(); 3048 CharSequence errorMessage; 3049 if (activeCall == null) { 3050 // Realistically this shouldn't happen, but best to handle gracefully 3051 errorMessage = mContext.getText(R.string.cant_call_due_to_ongoing_unknown_call); 3052 } else { 3053 errorMessage = mContext.getString(R.string.cant_call_due_to_ongoing_call, 3054 activeCall.getTargetPhoneAccountLabel()); 3055 } 3056 // Call is managed and there are ongoing self-managed calls. 3057 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR, 3058 errorMessage, errorMessage, "Ongoing call in another app.")); 3059 markCallAsRemoved(call); 3060 } 3061 3062 /** 3063 * Cleans up any calls currently associated with the specified connection service when the 3064 * service binder disconnects unexpectedly. 3065 * 3066 * @param service The connection service that disconnected. 3067 */ 3068 void handleConnectionServiceDeath(ConnectionServiceWrapper service) { 3069 if (service != null) { 3070 Log.i(this, "handleConnectionServiceDeath: service %s died", service); 3071 for (Call call : mCalls) { 3072 if (call.getConnectionService() == service) { 3073 if (call.getState() != CallState.DISCONNECTED) { 3074 markCallAsDisconnected(call, new DisconnectCause(DisconnectCause.ERROR, 3075 null /* message */, null /* description */, "CS_DEATH", 3076 ToneGenerator.TONE_PROP_PROMPT)); 3077 } 3078 markCallAsRemoved(call); 3079 } 3080 } 3081 } 3082 } 3083 3084 /** 3085 * Determines if the {@link CallsManager} has any non-external calls. 3086 * 3087 * @return {@code True} if there are any non-external calls, {@code false} otherwise. 3088 */ 3089 boolean hasAnyCalls() { 3090 if (mCalls.isEmpty()) { 3091 return false; 3092 } 3093 3094 for (Call call : mCalls) { 3095 if (!call.isExternalCall()) { 3096 return true; 3097 } 3098 } 3099 return false; 3100 } 3101 3102 boolean hasActiveOrHoldingCall() { 3103 return getFirstCallWithState(CallState.ACTIVE, CallState.ON_HOLD) != null; 3104 } 3105 3106 boolean hasRingingCall() { 3107 return getFirstCallWithState(CallState.RINGING, CallState.ANSWERED) != null; 3108 } 3109 3110 boolean hasRingingOrSimulatedRingingCall() { 3111 return getFirstCallWithState( 3112 CallState.SIMULATED_RINGING, CallState.RINGING, CallState.ANSWERED) != null; 3113 } 3114 3115 @VisibleForTesting 3116 public boolean onMediaButton(int type) { 3117 if (hasAnyCalls()) { 3118 Call ringingCall = getFirstCallWithState(CallState.RINGING, 3119 CallState.SIMULATED_RINGING); 3120 if (HeadsetMediaButton.SHORT_PRESS == type) { 3121 if (ringingCall == null) { 3122 Call activeCall = getFirstCallWithState(CallState.ACTIVE); 3123 Call onHoldCall = getFirstCallWithState(CallState.ON_HOLD); 3124 if (activeCall != null && onHoldCall != null) { 3125 // Two calls, short-press -> switch calls 3126 Log.addEvent(onHoldCall, LogUtils.Events.INFO, 3127 "two calls, media btn short press - switch call."); 3128 unholdCall(onHoldCall); 3129 return true; 3130 } 3131 3132 Call callToHangup = getFirstCallWithState(CallState.RINGING, CallState.DIALING, 3133 CallState.PULLING, CallState.ACTIVE, CallState.ON_HOLD); 3134 Log.addEvent(callToHangup, LogUtils.Events.INFO, 3135 "media btn short press - end call."); 3136 if (callToHangup != null) { 3137 disconnectCall(callToHangup); 3138 return true; 3139 } 3140 } else { 3141 answerCall(ringingCall, VideoProfile.STATE_AUDIO_ONLY); 3142 return true; 3143 } 3144 } else if (HeadsetMediaButton.LONG_PRESS == type) { 3145 if (ringingCall != null) { 3146 Log.addEvent(getForegroundCall(), 3147 LogUtils.Events.INFO, "media btn long press - reject"); 3148 ringingCall.reject(false, null); 3149 } else { 3150 Call activeCall = getFirstCallWithState(CallState.ACTIVE); 3151 Call onHoldCall = getFirstCallWithState(CallState.ON_HOLD); 3152 if (activeCall != null && onHoldCall != null) { 3153 // Two calls, long-press -> end current call 3154 Log.addEvent(activeCall, LogUtils.Events.INFO, 3155 "two calls, media btn long press - end current call."); 3156 disconnectCall(activeCall); 3157 return true; 3158 } 3159 3160 Log.addEvent(getForegroundCall(), LogUtils.Events.INFO, 3161 "media btn long press - mute"); 3162 mCallAudioManager.toggleMute(); 3163 } 3164 return true; 3165 } 3166 } 3167 return false; 3168 } 3169 3170 /** 3171 * Returns true if telecom supports adding another top-level call. 3172 */ 3173 @VisibleForTesting 3174 public boolean canAddCall() { 3175 boolean isDeviceProvisioned = Settings.Global.getInt(mContext.getContentResolver(), 3176 Settings.Global.DEVICE_PROVISIONED, 0) != 0; 3177 if (!isDeviceProvisioned) { 3178 Log.d(TAG, "Device not provisioned, canAddCall is false."); 3179 return false; 3180 } 3181 3182 if (getFirstCallWithState(OUTGOING_CALL_STATES) != null) { 3183 return false; 3184 } 3185 3186 int count = 0; 3187 for (Call call : mCalls) { 3188 if (call.isEmergencyCall()) { 3189 // We never support add call if one of the calls is an emergency call. 3190 return false; 3191 } else if (call.isExternalCall()) { 3192 // External calls don't count. 3193 continue; 3194 } else if (call.getParentCall() == null) { 3195 count++; 3196 } 3197 Bundle extras = call.getExtras(); 3198 if (extras != null) { 3199 if (extras.getBoolean(Connection.EXTRA_DISABLE_ADD_CALL, false)) { 3200 return false; 3201 } 3202 } 3203 3204 // We do not check states for canAddCall. We treat disconnected calls the same 3205 // and wait until they are removed instead. If we didn't count disconnected calls, 3206 // we could put InCallServices into a state where they are showing two calls but 3207 // also support add-call. Technically it's right, but overall looks better (UI-wise) 3208 // and acts better if we wait until the call is removed. 3209 if (count >= MAXIMUM_TOP_LEVEL_CALLS) { 3210 return false; 3211 } 3212 } 3213 3214 return true; 3215 } 3216 3217 @VisibleForTesting 3218 public Call getRingingOrSimulatedRingingCall() { 3219 return getFirstCallWithState(CallState.RINGING, 3220 CallState.ANSWERED, CallState.SIMULATED_RINGING); 3221 } 3222 3223 public Call getActiveCall() { 3224 return getFirstCallWithState(CallState.ACTIVE); 3225 } 3226 3227 Call getDialingCall() { 3228 return getFirstCallWithState(CallState.DIALING); 3229 } 3230 3231 @VisibleForTesting 3232 public Call getHeldCall() { 3233 return getFirstCallWithState(CallState.ON_HOLD); 3234 } 3235 3236 public Call getHeldCallByConnectionService(PhoneAccountHandle targetPhoneAccount) { 3237 Optional<Call> heldCall = mCalls.stream() 3238 .filter(call -> PhoneAccountHandle.areFromSamePackage(call.getTargetPhoneAccount(), 3239 targetPhoneAccount) 3240 && call.getParentCall() == null 3241 && call.getState() == CallState.ON_HOLD) 3242 .findFirst(); 3243 return heldCall.isPresent() ? heldCall.get() : null; 3244 } 3245 3246 @VisibleForTesting 3247 public int getNumHeldCalls() { 3248 int count = 0; 3249 for (Call call : mCalls) { 3250 if (call.getParentCall() == null && call.getState() == CallState.ON_HOLD) { 3251 count++; 3252 } 3253 } 3254 return count; 3255 } 3256 3257 @VisibleForTesting 3258 public Call getOutgoingCall() { 3259 return getFirstCallWithState(OUTGOING_CALL_STATES); 3260 } 3261 3262 @VisibleForTesting 3263 public Call getFirstCallWithState(int... states) { 3264 return getFirstCallWithState(null, states); 3265 } 3266 3267 @VisibleForTesting 3268 public PhoneNumberUtilsAdapter getPhoneNumberUtilsAdapter() { 3269 return mPhoneNumberUtilsAdapter; 3270 } 3271 3272 @VisibleForTesting 3273 public CompletableFuture<Call> getLatestPostSelectionProcessingFuture() { 3274 return mLatestPostSelectionProcessingFuture; 3275 } 3276 3277 @VisibleForTesting 3278 public CompletableFuture getLatestPreAccountSelectionFuture() { 3279 return mLatestPreAccountSelectionFuture; 3280 } 3281 3282 /** 3283 * Returns the first call that it finds with the given states. The states are treated as having 3284 * priority order so that any call with the first state will be returned before any call with 3285 * states listed later in the parameter list. 3286 * 3287 * @param callToSkip Call that this method should skip while searching 3288 */ 3289 Call getFirstCallWithState(Call callToSkip, int... states) { 3290 for (int currentState : states) { 3291 // check the foreground first 3292 Call foregroundCall = getForegroundCall(); 3293 if (foregroundCall != null && foregroundCall.getState() == currentState) { 3294 return foregroundCall; 3295 } 3296 3297 for (Call call : mCalls) { 3298 if (Objects.equals(callToSkip, call)) { 3299 continue; 3300 } 3301 3302 // Only operate on top-level calls 3303 if (call.getParentCall() != null) { 3304 continue; 3305 } 3306 3307 if (call.isExternalCall()) { 3308 continue; 3309 } 3310 3311 if (currentState == call.getState()) { 3312 return call; 3313 } 3314 } 3315 } 3316 return null; 3317 } 3318 3319 Call createConferenceCall( 3320 String callId, 3321 PhoneAccountHandle phoneAccount, 3322 ParcelableConference parcelableConference) { 3323 3324 // If the parceled conference specifies a connect time, use it; otherwise default to 0, 3325 // which is the default value for new Calls. 3326 long connectTime = 3327 parcelableConference.getConnectTimeMillis() == 3328 Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 : 3329 parcelableConference.getConnectTimeMillis(); 3330 long connectElapsedTime = 3331 parcelableConference.getConnectElapsedTimeMillis() == 3332 Conference.CONNECT_TIME_NOT_SPECIFIED ? 0 : 3333 parcelableConference.getConnectElapsedTimeMillis(); 3334 3335 int callDirection = Call.getRemappedCallDirection(parcelableConference.getCallDirection()); 3336 3337 PhoneAccountHandle connectionMgr = 3338 mPhoneAccountRegistrar.getSimCallManagerFromHandle(phoneAccount, 3339 mCurrentUserHandle); 3340 Call call = new Call( 3341 callId, 3342 mContext, 3343 this, 3344 mLock, 3345 mConnectionServiceRepository, 3346 mPhoneNumberUtilsAdapter, 3347 null /* handle */, 3348 null /* gatewayInfo */, 3349 connectionMgr, 3350 phoneAccount, 3351 callDirection, 3352 false /* forceAttachToExistingConnection */, 3353 true /* isConference */, 3354 connectTime, 3355 connectElapsedTime, 3356 mClockProxy, 3357 mToastFactory); 3358 3359 setCallState(call, Call.getStateFromConnectionState(parcelableConference.getState()), 3360 "new conference call"); 3361 call.setHandle(parcelableConference.getHandle(), 3362 parcelableConference.getHandlePresentation()); 3363 call.setConnectionCapabilities(parcelableConference.getConnectionCapabilities()); 3364 call.setConnectionProperties(parcelableConference.getConnectionProperties()); 3365 call.setVideoState(parcelableConference.getVideoState()); 3366 call.setVideoProvider(parcelableConference.getVideoProvider()); 3367 call.setStatusHints(parcelableConference.getStatusHints()); 3368 call.putExtras(Call.SOURCE_CONNECTION_SERVICE, parcelableConference.getExtras()); 3369 // In case this Conference was added via a ConnectionManager, keep track of the original 3370 // Connection ID as created by the originating ConnectionService. 3371 Bundle extras = parcelableConference.getExtras(); 3372 if (extras != null && extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 3373 call.setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID)); 3374 } 3375 3376 // TODO: Move this to be a part of addCall() 3377 call.addListener(this); 3378 addCall(call); 3379 return call; 3380 } 3381 3382 /** 3383 * @return the call state currently tracked by {@link PhoneStateBroadcaster} 3384 */ 3385 int getCallState() { 3386 return mPhoneStateBroadcaster.getCallState(); 3387 } 3388 3389 /** 3390 * Retrieves the {@link PhoneAccountRegistrar}. 3391 * 3392 * @return The {@link PhoneAccountRegistrar}. 3393 */ 3394 @VisibleForTesting 3395 public PhoneAccountRegistrar getPhoneAccountRegistrar() { 3396 return mPhoneAccountRegistrar; 3397 } 3398 3399 /** 3400 * Retrieves the {@link DisconnectedCallNotifier} 3401 * @return The {@link DisconnectedCallNotifier}. 3402 */ 3403 DisconnectedCallNotifier getDisconnectedCallNotifier() { 3404 return mDisconnectedCallNotifier; 3405 } 3406 3407 /** 3408 * Retrieves the {@link MissedCallNotifier} 3409 * @return The {@link MissedCallNotifier}. 3410 */ 3411 MissedCallNotifier getMissedCallNotifier() { 3412 return mMissedCallNotifier; 3413 } 3414 3415 /** 3416 * Retrieves the {@link IncomingCallNotifier}. 3417 * @return The {@link IncomingCallNotifier}. 3418 */ 3419 IncomingCallNotifier getIncomingCallNotifier() { 3420 return mIncomingCallNotifier; 3421 } 3422 3423 /** 3424 * Reject an incoming call and manually add it to the Call Log. 3425 * @param incomingCall Incoming call that has been rejected 3426 */ 3427 private void rejectCallAndLog(Call incomingCall, CallFilteringResult result) { 3428 if (incomingCall.getConnectionService() != null) { 3429 // Only reject the call if it has not already been destroyed. If a call ends while 3430 // incoming call filtering is taking place, it is possible that the call has already 3431 // been destroyed, and as such it will be impossible to send the reject to the 3432 // associated ConnectionService. 3433 incomingCall.reject(false, null); 3434 } else { 3435 Log.i(this, "rejectCallAndLog - call already destroyed."); 3436 } 3437 3438 // Since the call was not added to the list of calls, we have to call the missed 3439 // call notifier and the call logger manually. 3440 // Do we need missed call notification for direct to Voicemail calls? 3441 mCallLogManager.logCall(incomingCall, Calls.MISSED_TYPE, 3442 true /*showNotificationForMissedCall*/, result); 3443 } 3444 3445 /** 3446 * Adds the specified call to the main list of live calls. 3447 * 3448 * @param call The call to add. 3449 */ 3450 @VisibleForTesting 3451 public void addCall(Call call) { 3452 Trace.beginSection("addCall"); 3453 Log.v(this, "addCall(%s)", call); 3454 call.addListener(this); 3455 mCalls.add(call); 3456 3457 // Specifies the time telecom finished routing the call. This is used by the dialer for 3458 // analytics. 3459 Bundle extras = call.getIntentExtras(); 3460 extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_END_TIME_MILLIS, 3461 SystemClock.elapsedRealtime()); 3462 3463 updateCanAddCall(); 3464 updateHasActiveRttCall(); 3465 updateExternalCallCanPullSupport(); 3466 // onCallAdded for calls which immediately take the foreground (like the first call). 3467 for (CallsManagerListener listener : mListeners) { 3468 if (LogUtils.SYSTRACE_DEBUG) { 3469 Trace.beginSection(listener.getClass().toString() + " addCall"); 3470 } 3471 listener.onCallAdded(call); 3472 if (LogUtils.SYSTRACE_DEBUG) { 3473 Trace.endSection(); 3474 } 3475 } 3476 Trace.endSection(); 3477 } 3478 3479 @VisibleForTesting 3480 public void removeCall(Call call) { 3481 Trace.beginSection("removeCall"); 3482 Log.v(this, "removeCall(%s)", call); 3483 3484 call.setParentAndChildCall(null); // clean up parent relationship before destroying. 3485 call.removeListener(this); 3486 call.clearConnectionService(); 3487 // TODO: clean up RTT pipes 3488 3489 boolean shouldNotify = false; 3490 if (mCalls.contains(call)) { 3491 mCalls.remove(call); 3492 shouldNotify = true; 3493 } 3494 3495 call.destroy(); 3496 updateExternalCallCanPullSupport(); 3497 // Only broadcast changes for calls that are being tracked. 3498 if (shouldNotify) { 3499 updateCanAddCall(); 3500 updateHasActiveRttCall(); 3501 for (CallsManagerListener listener : mListeners) { 3502 if (LogUtils.SYSTRACE_DEBUG) { 3503 Trace.beginSection(listener.getClass().toString() + " onCallRemoved"); 3504 } 3505 listener.onCallRemoved(call); 3506 if (LogUtils.SYSTRACE_DEBUG) { 3507 Trace.endSection(); 3508 } 3509 } 3510 } 3511 Trace.endSection(); 3512 } 3513 3514 private void updateHasActiveRttCall() { 3515 boolean hasActiveRttCall = hasActiveRttCall(); 3516 if (hasActiveRttCall != mHasActiveRttCall) { 3517 Log.i(this, "updateHasActiveRttCall %s -> %s", mHasActiveRttCall, hasActiveRttCall); 3518 AudioManager.setRttEnabled(hasActiveRttCall); 3519 mHasActiveRttCall = hasActiveRttCall; 3520 } 3521 } 3522 3523 private boolean hasActiveRttCall() { 3524 for (Call call : mCalls) { 3525 if (call.isActive() && call.isRttCall()) { 3526 return true; 3527 } 3528 } 3529 return false; 3530 } 3531 3532 /** 3533 * Sets the specified state on the specified call. 3534 * 3535 * @param call The call. 3536 * @param newState The new state of the call. 3537 */ 3538 private void setCallState(Call call, int newState, String tag) { 3539 if (call == null) { 3540 return; 3541 } 3542 int oldState = call.getState(); 3543 Log.i(this, "setCallState %s -> %s, call: %s", 3544 CallState.toString(call.getParcelableCallState()), 3545 CallState.toString(newState), call); 3546 if (newState != oldState) { 3547 // If the call switches to held state while a DTMF tone is playing, stop the tone to 3548 // ensure that the tone generator stops playing the tone. 3549 if (newState == CallState.ON_HOLD && call.isDtmfTonePlaying()) { 3550 stopDtmfTone(call); 3551 } 3552 3553 // Unfortunately, in the telephony world the radio is king. So if the call notifies 3554 // us that the call is in a particular state, we allow it even if it doesn't make 3555 // sense (e.g., STATE_ACTIVE -> STATE_RINGING). 3556 // TODO: Consider putting a stop to the above and turning CallState 3557 // into a well-defined state machine. 3558 // TODO: Define expected state transitions here, and log when an 3559 // unexpected transition occurs. 3560 if (call.setState(newState, tag)) { 3561 if ((oldState != CallState.AUDIO_PROCESSING) && 3562 (newState == CallState.DISCONNECTED)) { 3563 maybeSendPostCallScreenIntent(call); 3564 } 3565 maybeShowErrorDialogOnDisconnect(call); 3566 3567 Trace.beginSection("onCallStateChanged"); 3568 3569 maybeHandleHandover(call, newState); 3570 3571 // Only broadcast state change for calls that are being tracked. 3572 if (mCalls.contains(call)) { 3573 updateCanAddCall(); 3574 updateHasActiveRttCall(); 3575 for (CallsManagerListener listener : mListeners) { 3576 if (LogUtils.SYSTRACE_DEBUG) { 3577 Trace.beginSection(listener.getClass().toString() + 3578 " onCallStateChanged"); 3579 } 3580 listener.onCallStateChanged(call, oldState, newState); 3581 if (LogUtils.SYSTRACE_DEBUG) { 3582 Trace.endSection(); 3583 } 3584 } 3585 } 3586 Trace.endSection(); 3587 } else { 3588 Log.i(this, "failed in setting the state to new state"); 3589 } 3590 } 3591 } 3592 3593 /** 3594 * Identifies call state transitions for a call which trigger handover events. 3595 * - If this call has a handover to it which just started and this call goes active, treat 3596 * this as if the user accepted the handover. 3597 * - If this call has a handover to it which just started and this call is disconnected, treat 3598 * this as if the user rejected the handover. 3599 * - If this call has a handover from it which just started and this call is disconnected, do 3600 * nothing as the call prematurely disconnected before the user accepted the handover. 3601 * - If this call has a handover from it which was already accepted by the user and this call is 3602 * disconnected, mark the handover as complete. 3603 * 3604 * @param call A call whose state is changing. 3605 * @param newState The new state of the call. 3606 */ 3607 private void maybeHandleHandover(Call call, int newState) { 3608 if (call.getHandoverSourceCall() != null) { 3609 // We are handing over another call to this one. 3610 if (call.getHandoverState() == HandoverState.HANDOVER_TO_STARTED) { 3611 // A handover to this call has just been initiated. 3612 if (newState == CallState.ACTIVE) { 3613 // This call went active, so the user has accepted the handover. 3614 Log.i(this, "setCallState: handover to accepted"); 3615 acceptHandoverTo(call); 3616 } else if (newState == CallState.DISCONNECTED) { 3617 // The call was disconnected, so the user has rejected the handover. 3618 Log.i(this, "setCallState: handover to rejected"); 3619 rejectHandoverTo(call); 3620 } 3621 } 3622 // If this call was disconnected because it was handed over TO another call, report the 3623 // handover as complete. 3624 } else if (call.getHandoverDestinationCall() != null 3625 && newState == CallState.DISCONNECTED) { 3626 int handoverState = call.getHandoverState(); 3627 if (handoverState == HandoverState.HANDOVER_FROM_STARTED) { 3628 // Disconnect before handover was accepted. 3629 Log.i(this, "setCallState: disconnect before handover accepted"); 3630 // Let the handover destination know that the source has disconnected prior to 3631 // completion of the handover. 3632 call.getHandoverDestinationCall().sendCallEvent( 3633 android.telecom.Call.EVENT_HANDOVER_SOURCE_DISCONNECTED, null); 3634 } else if (handoverState == HandoverState.HANDOVER_ACCEPTED) { 3635 Log.i(this, "setCallState: handover from complete"); 3636 completeHandoverFrom(call); 3637 } 3638 } 3639 } 3640 3641 private void completeHandoverFrom(Call call) { 3642 Call handoverTo = call.getHandoverDestinationCall(); 3643 Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", 3644 call.getId(), handoverTo.getId()); 3645 Log.addEvent(call, LogUtils.Events.HANDOVER_COMPLETE, "from=%s, to=%s", 3646 call.getId(), handoverTo.getId()); 3647 3648 // Inform the "from" Call (ie the source call) that the handover from it has 3649 // completed; this allows the InCallService to be notified that a handover it 3650 // initiated completed. 3651 call.onConnectionEvent(Connection.EVENT_HANDOVER_COMPLETE, null); 3652 call.onHandoverComplete(); 3653 3654 // Inform the "to" ConnectionService that handover to it has completed. 3655 handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_COMPLETE, null); 3656 handoverTo.onHandoverComplete(); 3657 answerCall(handoverTo, handoverTo.getVideoState()); 3658 call.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_COMPLETE); 3659 3660 // If the call we handed over to is self-managed, we need to disconnect the calls for other 3661 // ConnectionServices. 3662 if (handoverTo.isSelfManaged()) { 3663 disconnectOtherCalls(handoverTo.getTargetPhoneAccount()); 3664 } 3665 } 3666 3667 private void rejectHandoverTo(Call handoverTo) { 3668 Call handoverFrom = handoverTo.getHandoverSourceCall(); 3669 Log.i(this, "rejectHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId()); 3670 Log.addEvent(handoverFrom, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s, rejected", 3671 handoverTo.getId(), handoverFrom.getId()); 3672 Log.addEvent(handoverTo, LogUtils.Events.HANDOVER_FAILED, "from=%s, to=%s, rejected", 3673 handoverTo.getId(), handoverFrom.getId()); 3674 3675 // Inform the "from" Call (ie the source call) that the handover from it has 3676 // failed; this allows the InCallService to be notified that a handover it 3677 // initiated failed. 3678 handoverFrom.onConnectionEvent(Connection.EVENT_HANDOVER_FAILED, null); 3679 handoverFrom.onHandoverFailed(android.telecom.Call.Callback.HANDOVER_FAILURE_USER_REJECTED); 3680 3681 // Inform the "to" ConnectionService that handover to it has failed. This 3682 // allows the ConnectionService the call was being handed over 3683 if (handoverTo.getConnectionService() != null) { 3684 // Only attempt if the call has a bound ConnectionService if handover failed 3685 // early on in the handover process, the CS will be unbound and we won't be 3686 // able to send the call event. 3687 handoverTo.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null); 3688 handoverTo.getConnectionService().handoverFailed(handoverTo, 3689 android.telecom.Call.Callback.HANDOVER_FAILURE_USER_REJECTED); 3690 } 3691 handoverTo.markFinishedHandoverStateAndCleanup(HandoverState.HANDOVER_FAILED); 3692 } 3693 3694 private void acceptHandoverTo(Call handoverTo) { 3695 Call handoverFrom = handoverTo.getHandoverSourceCall(); 3696 Log.i(this, "acceptHandoverTo: from=%s, to=%s", handoverFrom.getId(), handoverTo.getId()); 3697 handoverTo.setHandoverState(HandoverState.HANDOVER_ACCEPTED); 3698 handoverTo.onHandoverComplete(); 3699 handoverFrom.setHandoverState(HandoverState.HANDOVER_ACCEPTED); 3700 handoverFrom.onHandoverComplete(); 3701 3702 Log.addEvent(handoverTo, LogUtils.Events.ACCEPT_HANDOVER, "from=%s, to=%s", 3703 handoverFrom.getId(), handoverTo.getId()); 3704 Log.addEvent(handoverFrom, LogUtils.Events.ACCEPT_HANDOVER, "from=%s, to=%s", 3705 handoverFrom.getId(), handoverTo.getId()); 3706 3707 // Disconnect the call we handed over from. 3708 disconnectCall(handoverFrom); 3709 // If we handed over to a self-managed ConnectionService, we need to disconnect calls for 3710 // other ConnectionServices. 3711 if (handoverTo.isSelfManaged()) { 3712 disconnectOtherCalls(handoverTo.getTargetPhoneAccount()); 3713 } 3714 } 3715 3716 private void updateCanAddCall() { 3717 boolean newCanAddCall = canAddCall(); 3718 if (newCanAddCall != mCanAddCall) { 3719 mCanAddCall = newCanAddCall; 3720 for (CallsManagerListener listener : mListeners) { 3721 if (LogUtils.SYSTRACE_DEBUG) { 3722 Trace.beginSection(listener.getClass().toString() + " updateCanAddCall"); 3723 } 3724 listener.onCanAddCallChanged(mCanAddCall); 3725 if (LogUtils.SYSTRACE_DEBUG) { 3726 Trace.endSection(); 3727 } 3728 } 3729 } 3730 } 3731 3732 private boolean isPotentialMMICode(Uri handle) { 3733 return (handle != null && handle.getSchemeSpecificPart() != null 3734 && handle.getSchemeSpecificPart().contains("#")); 3735 } 3736 3737 /** 3738 * Determines if a dialed number is potentially an In-Call MMI code. In-Call MMI codes are 3739 * MMI codes which can be dialed when one or more calls are in progress. 3740 * <P> 3741 * Checks for numbers formatted similar to the MMI codes defined in: 3742 * {@link com.android.internal.telephony.Phone#handleInCallMmiCommands(String)} 3743 * 3744 * @param handle The URI to call. 3745 * @return {@code True} if the URI represents a number which could be an in-call MMI code. 3746 */ 3747 private boolean isPotentialInCallMMICode(Uri handle) { 3748 if (handle != null && handle.getSchemeSpecificPart() != null && 3749 handle.getScheme() != null && 3750 handle.getScheme().equals(PhoneAccount.SCHEME_TEL)) { 3751 3752 String dialedNumber = handle.getSchemeSpecificPart(); 3753 return (dialedNumber.equals("0") || 3754 (dialedNumber.startsWith("1") && dialedNumber.length() <= 2) || 3755 (dialedNumber.startsWith("2") && dialedNumber.length() <= 2) || 3756 dialedNumber.equals("3") || 3757 dialedNumber.equals("4") || 3758 dialedNumber.equals("5")); 3759 } 3760 return false; 3761 } 3762 3763 @VisibleForTesting 3764 public int getNumCallsWithState(final boolean isSelfManaged, Call excludeCall, 3765 PhoneAccountHandle phoneAccountHandle, int... states) { 3766 return getNumCallsWithState(isSelfManaged ? CALL_FILTER_SELF_MANAGED : CALL_FILTER_MANAGED, 3767 excludeCall, phoneAccountHandle, states); 3768 } 3769 3770 /** 3771 * Determines the number of calls matching the specified criteria. 3772 * @param callFilter indicates whether to include just managed calls 3773 * ({@link #CALL_FILTER_MANAGED}), self-managed calls 3774 * ({@link #CALL_FILTER_SELF_MANAGED}), or all calls 3775 * ({@link #CALL_FILTER_ALL}). 3776 * @param excludeCall Where {@code non-null}, this call is excluded from the count. 3777 * @param phoneAccountHandle Where {@code non-null}, calls for this {@link PhoneAccountHandle} 3778 * are excluded from the count. 3779 * @param states The list of {@link CallState}s to include in the count. 3780 * @return Count of calls matching criteria. 3781 */ 3782 @VisibleForTesting 3783 public int getNumCallsWithState(final int callFilter, Call excludeCall, 3784 PhoneAccountHandle phoneAccountHandle, int... states) { 3785 3786 Set<Integer> desiredStates = IntStream.of(states).boxed().collect(Collectors.toSet()); 3787 3788 Stream<Call> callsStream = mCalls.stream() 3789 .filter(call -> desiredStates.contains(call.getState()) && 3790 call.getParentCall() == null && !call.isExternalCall()); 3791 3792 if (callFilter == CALL_FILTER_MANAGED) { 3793 callsStream = callsStream.filter(call -> !call.isSelfManaged()); 3794 } else if (callFilter == CALL_FILTER_SELF_MANAGED) { 3795 callsStream = callsStream.filter(call -> call.isSelfManaged()); 3796 } 3797 3798 // If a call to exclude was specified, filter it out. 3799 if (excludeCall != null) { 3800 callsStream = callsStream.filter(call -> call != excludeCall); 3801 } 3802 3803 // If a phone account handle was specified, only consider calls for that phone account. 3804 if (phoneAccountHandle != null) { 3805 callsStream = callsStream.filter( 3806 call -> phoneAccountHandle.equals(call.getTargetPhoneAccount())); 3807 } 3808 3809 return (int) callsStream.count(); 3810 } 3811 3812 private boolean hasMaximumLiveCalls(Call exceptCall) { 3813 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL, 3814 exceptCall, null /* phoneAccountHandle*/, LIVE_CALL_STATES); 3815 } 3816 3817 private boolean hasMaximumManagedLiveCalls(Call exceptCall) { 3818 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(false /* isSelfManaged */, 3819 exceptCall, null /* phoneAccountHandle */, LIVE_CALL_STATES); 3820 } 3821 3822 private boolean hasMaximumSelfManagedCalls(Call exceptCall, 3823 PhoneAccountHandle phoneAccountHandle) { 3824 return MAXIMUM_SELF_MANAGED_CALLS <= getNumCallsWithState(true /* isSelfManaged */, 3825 exceptCall, phoneAccountHandle, ANY_CALL_STATE); 3826 } 3827 3828 private boolean hasMaximumManagedHoldingCalls(Call exceptCall) { 3829 return MAXIMUM_HOLD_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, 3830 null /* phoneAccountHandle */, CallState.ON_HOLD); 3831 } 3832 3833 private boolean hasMaximumManagedRingingCalls(Call exceptCall) { 3834 return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, 3835 null /* phoneAccountHandle */, CallState.RINGING, CallState.ANSWERED); 3836 } 3837 3838 private boolean hasMaximumSelfManagedRingingCalls(Call exceptCall, 3839 PhoneAccountHandle phoneAccountHandle) { 3840 return MAXIMUM_RINGING_CALLS <= getNumCallsWithState(true /* isSelfManaged */, exceptCall, 3841 phoneAccountHandle, CallState.RINGING, CallState.ANSWERED); 3842 } 3843 3844 private boolean hasMaximumOutgoingCalls(Call exceptCall) { 3845 return MAXIMUM_LIVE_CALLS <= getNumCallsWithState(CALL_FILTER_ALL, 3846 exceptCall, null /* phoneAccountHandle */, OUTGOING_CALL_STATES); 3847 } 3848 3849 private boolean hasMaximumManagedOutgoingCalls(Call exceptCall) { 3850 return MAXIMUM_OUTGOING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, 3851 null /* phoneAccountHandle */, OUTGOING_CALL_STATES); 3852 } 3853 3854 private boolean hasMaximumManagedDialingCalls(Call exceptCall) { 3855 return MAXIMUM_DIALING_CALLS <= getNumCallsWithState(false /* isSelfManaged */, exceptCall, 3856 null /* phoneAccountHandle */, CallState.DIALING, CallState.PULLING); 3857 } 3858 3859 /** 3860 * Given a {@link PhoneAccountHandle} determines if there are other unholdable calls owned by 3861 * another connection service. 3862 * @param phoneAccountHandle The {@link PhoneAccountHandle} to check. 3863 * @return {@code true} if there are other unholdable calls, {@code false} otherwise. 3864 */ 3865 public boolean hasUnholdableCallsForOtherConnectionService( 3866 PhoneAccountHandle phoneAccountHandle) { 3867 return getNumUnholdableCallsForOtherConnectionService(phoneAccountHandle) > 0; 3868 } 3869 3870 /** 3871 * Determines the number of unholdable calls present in a connection service other than the one 3872 * the passed phone account belonds to. 3873 * @param phoneAccountHandle The handle of the PhoneAccount. 3874 * @return Number of unholdable calls owned by other connection service. 3875 */ 3876 public int getNumUnholdableCallsForOtherConnectionService( 3877 PhoneAccountHandle phoneAccountHandle) { 3878 return (int) mCalls.stream().filter(call -> 3879 !phoneAccountHandle.getComponentName().equals( 3880 call.getTargetPhoneAccount().getComponentName()) 3881 && call.getParentCall() == null 3882 && !call.isExternalCall() 3883 && !canHold(call)).count(); 3884 } 3885 3886 /** 3887 * Determines if there are any managed calls. 3888 * @return {@code true} if there are managed calls, {@code false} otherwise. 3889 */ 3890 public boolean hasManagedCalls() { 3891 return mCalls.stream().filter(call -> !call.isSelfManaged() && 3892 !call.isExternalCall()).count() > 0; 3893 } 3894 3895 /** 3896 * Determines if there are any self-managed calls. 3897 * @return {@code true} if there are self-managed calls, {@code false} otherwise. 3898 */ 3899 public boolean hasSelfManagedCalls() { 3900 return mCalls.stream().filter(call -> call.isSelfManaged()).count() > 0; 3901 } 3902 3903 /** 3904 * Determines if there are any ongoing managed or self-managed calls. 3905 * Note: The {@link #ONGOING_CALL_STATES} are 3906 * @return {@code true} if there are ongoing managed or self-managed calls, {@code false} 3907 * otherwise. 3908 */ 3909 public boolean hasOngoingCalls() { 3910 return getNumCallsWithState( 3911 CALL_FILTER_ALL, null /* excludeCall */, 3912 null /* phoneAccountHandle */, 3913 ONGOING_CALL_STATES) > 0; 3914 } 3915 3916 /** 3917 * Determines if there are any ongoing managed calls. 3918 * @return {@code true} if there are ongoing managed calls, {@code false} otherwise. 3919 */ 3920 public boolean hasOngoingManagedCalls() { 3921 return getNumCallsWithState( 3922 CALL_FILTER_MANAGED, null /* excludeCall */, 3923 null /* phoneAccountHandle */, 3924 ONGOING_CALL_STATES) > 0; 3925 } 3926 3927 /** 3928 * Determines if the system incoming call UI should be shown. 3929 * The system incoming call UI will be shown if the new incoming call is self-managed, and there 3930 * are ongoing calls for another PhoneAccount. 3931 * @param incomingCall The incoming call. 3932 * @return {@code true} if the system incoming call UI should be shown, {@code false} otherwise. 3933 */ 3934 public boolean shouldShowSystemIncomingCallUi(Call incomingCall) { 3935 return incomingCall.isIncoming() && incomingCall.isSelfManaged() 3936 && hasUnholdableCallsForOtherConnectionService(incomingCall.getTargetPhoneAccount()) 3937 && incomingCall.getHandoverSourceCall() == null; 3938 } 3939 3940 @VisibleForTesting 3941 public boolean makeRoomForOutgoingEmergencyCall(Call emergencyCall) { 3942 // Always disconnect any ringing/incoming calls when an emergency call is placed to minimize 3943 // distraction. This does not affect live call count. 3944 if (hasRingingOrSimulatedRingingCall()) { 3945 Call ringingCall = getRingingOrSimulatedRingingCall(); 3946 ringingCall.getAnalytics().setCallIsAdditional(true); 3947 ringingCall.getAnalytics().setCallIsInterrupted(true); 3948 if (ringingCall.getState() == CallState.SIMULATED_RINGING) { 3949 if (!ringingCall.hasGoneActiveBefore()) { 3950 // If this is an incoming call that is currently in SIMULATED_RINGING only 3951 // after a call screen, disconnect to make room and mark as missed, since 3952 // the user didn't get a chance to accept/reject. 3953 ringingCall.disconnect("emergency call dialed during simulated ringing " 3954 + "after screen."); 3955 } else { 3956 // If this is a simulated ringing call after being active and put in 3957 // AUDIO_PROCESSING state again, disconnect normally. 3958 ringingCall.reject(false, null, "emergency call dialed during simulated " 3959 + "ringing."); 3960 } 3961 } else { // normal incoming ringing call. 3962 // Hang up the ringing call to make room for the emergency call and mark as missed, 3963 // since the user did not reject. 3964 ringingCall.setOverrideDisconnectCauseCode( 3965 new DisconnectCause(DisconnectCause.MISSED)); 3966 ringingCall.reject(false, null, "emergency call dialed during ringing."); 3967 } 3968 } 3969 3970 // There is already room! 3971 if (!hasMaximumLiveCalls(emergencyCall)) return true; 3972 3973 Call liveCall = getFirstCallWithState(LIVE_CALL_STATES); 3974 Log.i(this, "makeRoomForOutgoingEmergencyCall call = " + emergencyCall 3975 + " livecall = " + liveCall); 3976 3977 if (emergencyCall == liveCall) { 3978 // Not likely, but a good sanity check. 3979 return true; 3980 } 3981 3982 if (hasMaximumOutgoingCalls(emergencyCall)) { 3983 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES); 3984 if (!outgoingCall.isEmergencyCall()) { 3985 emergencyCall.getAnalytics().setCallIsAdditional(true); 3986 outgoingCall.getAnalytics().setCallIsInterrupted(true); 3987 outgoingCall.disconnect("Disconnecting dialing call in favor of new dialing" 3988 + " emergency call."); 3989 return true; 3990 } 3991 if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) { 3992 // Sanity check: if there is an orphaned emergency call in the 3993 // {@link CallState#SELECT_PHONE_ACCOUNT} state, just disconnect it since the user 3994 // has explicitly started a new call. 3995 emergencyCall.getAnalytics().setCallIsAdditional(true); 3996 outgoingCall.getAnalytics().setCallIsInterrupted(true); 3997 outgoingCall.disconnect("Disconnecting call in SELECT_PHONE_ACCOUNT in favor" 3998 + " of new outgoing call."); 3999 return true; 4000 } 4001 // If the user tries to make two outgoing calls to different emergency call numbers, 4002 // we will try to connect the first outgoing call and reject the second. 4003 return false; 4004 } 4005 4006 if (liveCall.getState() == CallState.AUDIO_PROCESSING) { 4007 emergencyCall.getAnalytics().setCallIsAdditional(true); 4008 liveCall.getAnalytics().setCallIsInterrupted(true); 4009 liveCall.disconnect("disconnecting audio processing call for emergency"); 4010 return true; 4011 } 4012 4013 // If we have the max number of held managed calls and we're placing an emergency call, 4014 // we'll disconnect the ongoing call if it cannot be held. 4015 if (hasMaximumManagedHoldingCalls(emergencyCall) && !canHold(liveCall)) { 4016 emergencyCall.getAnalytics().setCallIsAdditional(true); 4017 liveCall.getAnalytics().setCallIsInterrupted(true); 4018 // Disconnect the active call instead of the holding call because it is historically 4019 // easier to do, rather than disconnect a held call. 4020 liveCall.disconnect("disconnecting to make room for emergency call " 4021 + emergencyCall.getId()); 4022 return true; 4023 } 4024 4025 // TODO: Remove once b/23035408 has been corrected. 4026 // If the live call is a conference, it will not have a target phone account set. This 4027 // means the check to see if the live call has the same target phone account as the new 4028 // call will not cause us to bail early. As a result, we'll end up holding the 4029 // ongoing conference call. However, the ConnectionService is already doing that. This 4030 // has caused problems with some carriers. As a workaround until b/23035408 is 4031 // corrected, we will try and get the target phone account for one of the conference's 4032 // children and use that instead. 4033 PhoneAccountHandle liveCallPhoneAccount = liveCall.getTargetPhoneAccount(); 4034 if (liveCallPhoneAccount == null && liveCall.isConference() && 4035 !liveCall.getChildCalls().isEmpty()) { 4036 liveCallPhoneAccount = getFirstChildPhoneAccount(liveCall); 4037 Log.i(this, "makeRoomForOutgoingEmergencyCall: using child call PhoneAccount = " + 4038 liveCallPhoneAccount); 4039 } 4040 4041 // We may not know which PhoneAccount the emergency call will be placed on yet, but if 4042 // the liveCall PhoneAccount does not support placing emergency calls, then we know it 4043 // will not be that one and we do not want multiple PhoneAccounts active during an 4044 // emergency call if possible. Disconnect the active call in favor of the emergency call 4045 // instead of trying to hold. 4046 if (liveCall.getTargetPhoneAccount() != null) { 4047 PhoneAccount pa = mPhoneAccountRegistrar.getPhoneAccountUnchecked( 4048 liveCall.getTargetPhoneAccount()); 4049 if((pa.getCapabilities() & PhoneAccount.CAPABILITY_PLACE_EMERGENCY_CALLS) == 0) { 4050 liveCall.setOverrideDisconnectCauseCode(new DisconnectCause( 4051 DisconnectCause.LOCAL, DisconnectCause.REASON_EMERGENCY_CALL_PLACED)); 4052 liveCall.disconnect("outgoing call does not support emergency calls, " 4053 + "disconnecting."); 4054 } 4055 return true; 4056 } 4057 4058 // First thing, if we are trying to make an emergency call with the same package name as 4059 // the live call, then allow it so that the connection service can make its own decision 4060 // about how to handle the new call relative to the current one. 4061 // By default, for telephony, it will try to hold the existing call before placing the new 4062 // emergency call except for if the carrier does not support holding calls for emergency. 4063 // In this case, telephony will disconnect the call. 4064 if (PhoneAccountHandle.areFromSamePackage(liveCallPhoneAccount, 4065 emergencyCall.getTargetPhoneAccount())) { 4066 Log.i(this, "makeRoomForOutgoingEmergencyCall: phoneAccount matches."); 4067 emergencyCall.getAnalytics().setCallIsAdditional(true); 4068 liveCall.getAnalytics().setCallIsInterrupted(true); 4069 return true; 4070 } else if (emergencyCall.getTargetPhoneAccount() == null) { 4071 // Without a phone account, we can't say reliably that the call will fail. 4072 // If the user chooses the same phone account as the live call, then it's 4073 // still possible that the call can be made (like with CDMA calls not supporting 4074 // hold but they still support adding a call by going immediately into conference 4075 // mode). Return true here and we'll run this code again after user chooses an 4076 // account. 4077 return true; 4078 } 4079 4080 // Hold the live call if possible before attempting the new outgoing emergency call. 4081 if (canHold(liveCall)) { 4082 Log.i(this, "makeRoomForOutgoingEmergencyCall: holding live call."); 4083 emergencyCall.getAnalytics().setCallIsAdditional(true); 4084 liveCall.getAnalytics().setCallIsInterrupted(true); 4085 liveCall.hold("calling " + emergencyCall.getId()); 4086 return true; 4087 } 4088 4089 // The live call cannot be held so we're out of luck here. There's no room. 4090 return false; 4091 } 4092 4093 private boolean makeRoomForOutgoingCall(Call call) { 4094 // Already room! 4095 if (!hasMaximumLiveCalls(call)) return true; 4096 4097 // NOTE: If the amount of live calls changes beyond 1, this logic will probably 4098 // have to change. 4099 Call liveCall = getFirstCallWithState(LIVE_CALL_STATES); 4100 Log.i(this, "makeRoomForOutgoingCall call = " + call + " livecall = " + 4101 liveCall); 4102 4103 if (call == liveCall) { 4104 // If the call is already the foreground call, then we are golden. 4105 // This can happen after the user selects an account in the SELECT_PHONE_ACCOUNT 4106 // state since the call was already populated into the list. 4107 return true; 4108 } 4109 4110 if (hasMaximumOutgoingCalls(call)) { 4111 Call outgoingCall = getFirstCallWithState(OUTGOING_CALL_STATES); 4112 if (outgoingCall.getState() == CallState.SELECT_PHONE_ACCOUNT) { 4113 // If there is an orphaned call in the {@link CallState#SELECT_PHONE_ACCOUNT} 4114 // state, just disconnect it since the user has explicitly started a new call. 4115 call.getAnalytics().setCallIsAdditional(true); 4116 outgoingCall.getAnalytics().setCallIsInterrupted(true); 4117 outgoingCall.disconnect("Disconnecting call in SELECT_PHONE_ACCOUNT in favor" 4118 + " of new outgoing call."); 4119 return true; 4120 } 4121 return false; 4122 } 4123 4124 // TODO: Remove once b/23035408 has been corrected. 4125 // If the live call is a conference, it will not have a target phone account set. This 4126 // means the check to see if the live call has the same target phone account as the new 4127 // call will not cause us to bail early. As a result, we'll end up holding the 4128 // ongoing conference call. However, the ConnectionService is already doing that. This 4129 // has caused problems with some carriers. As a workaround until b/23035408 is 4130 // corrected, we will try and get the target phone account for one of the conference's 4131 // children and use that instead. 4132 PhoneAccountHandle liveCallPhoneAccount = liveCall.getTargetPhoneAccount(); 4133 if (liveCallPhoneAccount == null && liveCall.isConference() && 4134 !liveCall.getChildCalls().isEmpty()) { 4135 liveCallPhoneAccount = getFirstChildPhoneAccount(liveCall); 4136 Log.i(this, "makeRoomForOutgoingCall: using child call PhoneAccount = " + 4137 liveCallPhoneAccount); 4138 } 4139 4140 // First thing, if we are trying to make a call with the same phone account as the live 4141 // call, then allow it so that the connection service can make its own decision about 4142 // how to handle the new call relative to the current one. 4143 if (PhoneAccountHandle.areFromSamePackage(liveCallPhoneAccount, 4144 call.getTargetPhoneAccount())) { 4145 Log.i(this, "makeRoomForOutgoingCall: phoneAccount matches."); 4146 call.getAnalytics().setCallIsAdditional(true); 4147 liveCall.getAnalytics().setCallIsInterrupted(true); 4148 return true; 4149 } else if (call.getTargetPhoneAccount() == null) { 4150 // Without a phone account, we can't say reliably that the call will fail. 4151 // If the user chooses the same phone account as the live call, then it's 4152 // still possible that the call can be made (like with CDMA calls not supporting 4153 // hold but they still support adding a call by going immediately into conference 4154 // mode). Return true here and we'll run this code again after user chooses an 4155 // account. 4156 return true; 4157 } 4158 4159 // Try to hold the live call before attempting the new outgoing call. 4160 if (canHold(liveCall)) { 4161 Log.i(this, "makeRoomForOutgoingCall: holding live call."); 4162 call.getAnalytics().setCallIsAdditional(true); 4163 liveCall.getAnalytics().setCallIsInterrupted(true); 4164 liveCall.hold("calling " + call.getId()); 4165 return true; 4166 } 4167 4168 // The live call cannot be held so we're out of luck here. There's no room. 4169 return false; 4170 } 4171 4172 /** 4173 * Given a call, find the first non-null phone account handle of its children. 4174 * 4175 * @param parentCall The parent call. 4176 * @return The first non-null phone account handle of the children, or {@code null} if none. 4177 */ 4178 private PhoneAccountHandle getFirstChildPhoneAccount(Call parentCall) { 4179 for (Call childCall : parentCall.getChildCalls()) { 4180 PhoneAccountHandle childPhoneAccount = childCall.getTargetPhoneAccount(); 4181 if (childPhoneAccount != null) { 4182 return childPhoneAccount; 4183 } 4184 } 4185 return null; 4186 } 4187 4188 /** 4189 * Checks to see if the call should be on speakerphone and if so, set it. 4190 */ 4191 private void maybeMoveToSpeakerPhone(Call call) { 4192 if (call.isHandoverInProgress() && call.getState() == CallState.DIALING) { 4193 // When a new outgoing call is initiated for the purpose of handing over, do not engage 4194 // speaker automatically until the call goes active. 4195 return; 4196 } 4197 if (call.getStartWithSpeakerphoneOn()) { 4198 setAudioRoute(CallAudioState.ROUTE_SPEAKER, null); 4199 call.setStartWithSpeakerphoneOn(false); 4200 } 4201 } 4202 4203 /** 4204 * Checks to see if the call is an emergency call and if so, turn off mute. 4205 */ 4206 private void maybeTurnOffMute(Call call) { 4207 if (call.isEmergencyCall()) { 4208 mute(false); 4209 } 4210 } 4211 4212 private void ensureCallAudible() { 4213 AudioManager am = mContext.getSystemService(AudioManager.class); 4214 if (am == null) { 4215 Log.w(this, "ensureCallAudible: audio manager is null"); 4216 return; 4217 } 4218 if (am.getStreamVolume(AudioManager.STREAM_VOICE_CALL) == 0) { 4219 Log.i(this, "ensureCallAudible: voice call stream has volume 0. Adjusting to default."); 4220 am.setStreamVolume(AudioManager.STREAM_VOICE_CALL, 4221 AudioSystem.getDefaultStreamVolume(AudioManager.STREAM_VOICE_CALL), 0); 4222 } 4223 } 4224 4225 /** 4226 * Creates a new call for an existing connection. 4227 * 4228 * @param callId The id of the new call. 4229 * @param connection The connection information. 4230 * @return The new call. 4231 */ 4232 Call createCallForExistingConnection(String callId, ParcelableConnection connection) { 4233 boolean isDowngradedConference = (connection.getConnectionProperties() 4234 & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0; 4235 4236 PhoneAccountHandle connectionMgr = 4237 mPhoneAccountRegistrar.getSimCallManagerFromHandle(connection.getPhoneAccount(), 4238 mCurrentUserHandle); 4239 Call call = new Call( 4240 callId, 4241 mContext, 4242 this, 4243 mLock, 4244 mConnectionServiceRepository, 4245 mPhoneNumberUtilsAdapter, 4246 connection.getHandle() /* handle */, 4247 null /* gatewayInfo */, 4248 connectionMgr, 4249 connection.getPhoneAccount(), /* targetPhoneAccountHandle */ 4250 Call.getRemappedCallDirection(connection.getCallDirection()) /* callDirection */, 4251 false /* forceAttachToExistingConnection */, 4252 isDowngradedConference /* isConference */, 4253 connection.getConnectTimeMillis() /* connectTimeMillis */, 4254 connection.getConnectElapsedTimeMillis(), /* connectElapsedTimeMillis */ 4255 mClockProxy, 4256 mToastFactory); 4257 4258 call.initAnalytics(); 4259 call.getAnalytics().setCreatedFromExistingConnection(true); 4260 4261 setCallState(call, Call.getStateFromConnectionState(connection.getState()), 4262 "existing connection"); 4263 call.setVideoState(connection.getVideoState()); 4264 call.setConnectionCapabilities(connection.getConnectionCapabilities()); 4265 call.setConnectionProperties(connection.getConnectionProperties()); 4266 call.setHandle(connection.getHandle(), connection.getHandlePresentation()); 4267 call.setCallerDisplayName(connection.getCallerDisplayName(), 4268 connection.getCallerDisplayNamePresentation()); 4269 call.addListener(this); 4270 call.putExtras(Call.SOURCE_CONNECTION_SERVICE, connection.getExtras()); 4271 4272 Log.i(this, "createCallForExistingConnection: %s", connection); 4273 Call parentCall = null; 4274 if (!TextUtils.isEmpty(connection.getParentCallId())) { 4275 String parentId = connection.getParentCallId(); 4276 parentCall = mCalls 4277 .stream() 4278 .filter(c -> c.getId().equals(parentId)) 4279 .findFirst() 4280 .orElse(null); 4281 if (parentCall != null) { 4282 Log.i(this, "createCallForExistingConnection: %s added as child of %s.", 4283 call.getId(), 4284 parentCall.getId()); 4285 // Set JUST the parent property, which won't send an update to the Incall UI. 4286 call.setParentCall(parentCall); 4287 } 4288 } 4289 addCall(call); 4290 if (parentCall != null) { 4291 // Now, set the call as a child of the parent since it has been added to Telecom. This 4292 // is where we will inform InCall. 4293 call.setChildOf(parentCall); 4294 call.notifyParentChanged(parentCall); 4295 } 4296 4297 return call; 4298 } 4299 4300 /** 4301 * Determines whether Telecom already knows about a Connection added via the 4302 * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle, 4303 * Connection)} API via a ConnectionManager. 4304 * 4305 * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}. 4306 * @param originalConnectionId The new connection ID to check. 4307 * @return {@code true} if this connection is already known by Telecom. 4308 */ 4309 Call getAlreadyAddedConnection(String originalConnectionId) { 4310 Optional<Call> existingCall = mCalls.stream() 4311 .filter(call -> originalConnectionId.equals(call.getOriginalConnectionId()) || 4312 originalConnectionId.equals(call.getId())) 4313 .findFirst(); 4314 4315 if (existingCall.isPresent()) { 4316 Log.i(this, "isExistingConnectionAlreadyAdded - call %s already added with id %s", 4317 originalConnectionId, existingCall.get().getId()); 4318 return existingCall.get(); 4319 } 4320 4321 return null; 4322 } 4323 4324 /** 4325 * @return A new unique telecom call Id. 4326 */ 4327 private String getNextCallId() { 4328 synchronized(mLock) { 4329 return TELECOM_CALL_ID_PREFIX + (++mCallId); 4330 } 4331 } 4332 4333 public int getNextRttRequestId() { 4334 synchronized (mLock) { 4335 return (++mRttRequestId); 4336 } 4337 } 4338 4339 /** 4340 * Callback when foreground user is switched. We will reload missed call in all profiles 4341 * including the user itself. There may be chances that profiles are not started yet. 4342 */ 4343 @VisibleForTesting 4344 public void onUserSwitch(UserHandle userHandle) { 4345 mCurrentUserHandle = userHandle; 4346 mMissedCallNotifier.setCurrentUserHandle(userHandle); 4347 mRoleManagerAdapter.setCurrentUserHandle(userHandle); 4348 final UserManager userManager = UserManager.get(mContext); 4349 List<UserInfo> profiles = userManager.getEnabledProfiles(userHandle.getIdentifier()); 4350 for (UserInfo profile : profiles) { 4351 reloadMissedCallsOfUser(profile.getUserHandle()); 4352 } 4353 } 4354 4355 /** 4356 * Because there may be chances that profiles are not started yet though its parent user is 4357 * switched, we reload missed calls of profile that are just started here. 4358 */ 4359 void onUserStarting(UserHandle userHandle) { 4360 if (UserUtil.isProfile(mContext, userHandle)) { 4361 reloadMissedCallsOfUser(userHandle); 4362 } 4363 } 4364 4365 public TelecomSystem.SyncRoot getLock() { 4366 return mLock; 4367 } 4368 4369 public Timeouts.Adapter getTimeoutsAdapter() { 4370 return mTimeoutsAdapter; 4371 } 4372 4373 public SystemStateHelper getSystemStateHelper() { 4374 return mSystemStateHelper; 4375 } 4376 4377 private void reloadMissedCallsOfUser(UserHandle userHandle) { 4378 mMissedCallNotifier.reloadFromDatabase(mCallerInfoLookupHelper, 4379 new MissedCallNotifier.CallInfoFactory(), userHandle); 4380 } 4381 4382 public void onBootCompleted() { 4383 mMissedCallNotifier.reloadAfterBootComplete(mCallerInfoLookupHelper, 4384 new MissedCallNotifier.CallInfoFactory()); 4385 } 4386 4387 public boolean isIncomingCallPermitted(PhoneAccountHandle phoneAccountHandle) { 4388 return isIncomingCallPermitted(null /* excludeCall */, phoneAccountHandle); 4389 } 4390 4391 public boolean isIncomingCallPermitted(Call excludeCall, 4392 PhoneAccountHandle phoneAccountHandle) { 4393 if (phoneAccountHandle == null) { 4394 return false; 4395 } 4396 PhoneAccount phoneAccount = 4397 mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle); 4398 if (phoneAccount == null) { 4399 return false; 4400 } 4401 if (isInEmergencyCall()) return false; 4402 4403 if (!phoneAccount.isSelfManaged()) { 4404 return !hasMaximumManagedRingingCalls(excludeCall) && 4405 !hasMaximumManagedHoldingCalls(excludeCall); 4406 } else { 4407 return !hasMaximumSelfManagedRingingCalls(excludeCall, phoneAccountHandle) && 4408 !hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle); 4409 } 4410 } 4411 4412 public boolean isOutgoingCallPermitted(PhoneAccountHandle phoneAccountHandle) { 4413 return isOutgoingCallPermitted(null /* excludeCall */, phoneAccountHandle); 4414 } 4415 4416 public boolean isOutgoingCallPermitted(Call excludeCall, 4417 PhoneAccountHandle phoneAccountHandle) { 4418 if (phoneAccountHandle == null) { 4419 return false; 4420 } 4421 PhoneAccount phoneAccount = 4422 mPhoneAccountRegistrar.getPhoneAccountUnchecked(phoneAccountHandle); 4423 if (phoneAccount == null) { 4424 return false; 4425 } 4426 4427 if (!phoneAccount.isSelfManaged()) { 4428 return !hasMaximumManagedOutgoingCalls(excludeCall) && 4429 !hasMaximumManagedDialingCalls(excludeCall) && 4430 !hasMaximumManagedLiveCalls(excludeCall) && 4431 !hasMaximumManagedHoldingCalls(excludeCall); 4432 } else { 4433 // Only permit self-managed outgoing calls if 4434 // 1. there is no emergency ongoing call 4435 // 2. The outgoing call is an handover call or it not hit the self-managed call limit 4436 // and the current active call can be held. 4437 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); 4438 return !isInEmergencyCall() && 4439 ((excludeCall != null && excludeCall.getHandoverSourceCall() != null) || 4440 (!hasMaximumSelfManagedCalls(excludeCall, phoneAccountHandle) && 4441 (activeCall == null || canHold(activeCall)))); 4442 } 4443 } 4444 4445 public boolean isReplyWithSmsAllowed(int uid) { 4446 UserHandle callingUser = UserHandle.of(UserHandle.getUserId(uid)); 4447 UserManager userManager = mContext.getSystemService(UserManager.class); 4448 KeyguardManager keyguardManager = mContext.getSystemService(KeyguardManager.class); 4449 4450 boolean isUserRestricted = userManager != null 4451 && userManager.hasUserRestriction(UserManager.DISALLOW_SMS, callingUser); 4452 boolean isLockscreenRestricted = keyguardManager != null 4453 && keyguardManager.isDeviceLocked(); 4454 Log.d(this, "isReplyWithSmsAllowed: isUserRestricted: %s, isLockscreenRestricted: %s", 4455 isUserRestricted, isLockscreenRestricted); 4456 4457 // TODO(hallliu): actually check the lockscreen once b/77731473 is fixed 4458 return !isUserRestricted; 4459 } 4460 /** 4461 * Blocks execution until all Telecom handlers have completed their current work. 4462 */ 4463 public void waitOnHandlers() { 4464 CountDownLatch mainHandlerLatch = new CountDownLatch(3); 4465 mHandler.post(() -> { 4466 mainHandlerLatch.countDown(); 4467 }); 4468 mCallAudioManager.getCallAudioModeStateMachine().getHandler().post(() -> { 4469 mainHandlerLatch.countDown(); 4470 }); 4471 mCallAudioManager.getCallAudioRouteStateMachine().getHandler().post(() -> { 4472 mainHandlerLatch.countDown(); 4473 }); 4474 4475 try { 4476 mainHandlerLatch.await(HANDLER_WAIT_TIMEOUT, TimeUnit.MILLISECONDS); 4477 } catch (InterruptedException e) { 4478 Log.w(this, "waitOnHandlers: interrupted %s", e); 4479 } 4480 } 4481 4482 /** 4483 * Used to confirm creation of an outgoing call which was marked as pending confirmation in 4484 * {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent, String)}. 4485 * Called via {@link TelecomBroadcastIntentProcessor} for a call which was confirmed via 4486 * {@link ConfirmCallDialogActivity}. 4487 * @param callId The call ID of the call to confirm. 4488 */ 4489 public void confirmPendingCall(String callId) { 4490 Log.i(this, "confirmPendingCall: callId=%s", callId); 4491 if (mPendingCall != null && mPendingCall.getId().equals(callId)) { 4492 Log.addEvent(mPendingCall, LogUtils.Events.USER_CONFIRMED); 4493 4494 // We are going to place the new outgoing call, so disconnect any ongoing self-managed 4495 // calls which are ongoing at this time. 4496 disconnectSelfManagedCalls("outgoing call " + callId); 4497 4498 mPendingCallConfirm.complete(mPendingCall); 4499 mPendingCallConfirm = null; 4500 mPendingCall = null; 4501 } 4502 } 4503 4504 /** 4505 * Used to cancel an outgoing call which was marked as pending confirmation in 4506 * {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent, String)}. 4507 * Called via {@link TelecomBroadcastIntentProcessor} for a call which was confirmed via 4508 * {@link ConfirmCallDialogActivity}. 4509 * @param callId The call ID of the call to cancel. 4510 */ 4511 public void cancelPendingCall(String callId) { 4512 Log.i(this, "cancelPendingCall: callId=%s", callId); 4513 if (mPendingCall != null && mPendingCall.getId().equals(callId)) { 4514 Log.addEvent(mPendingCall, LogUtils.Events.USER_CANCELLED); 4515 markCallAsDisconnected(mPendingCall, new DisconnectCause(DisconnectCause.CANCELED)); 4516 markCallAsRemoved(mPendingCall); 4517 mPendingCall = null; 4518 mPendingCallConfirm.complete(null); 4519 mPendingCallConfirm = null; 4520 } 4521 } 4522 4523 /** 4524 * Called from {@link #startOutgoingCall(Uri, PhoneAccountHandle, Bundle, UserHandle, Intent, String)} when 4525 * a managed call is added while there are ongoing self-managed calls. Starts 4526 * {@link ConfirmCallDialogActivity} to prompt the user to see if they wish to place the 4527 * outgoing call or not. 4528 * @param call The call to confirm. 4529 */ 4530 private void startCallConfirmation(Call call, CompletableFuture<Call> confirmationFuture) { 4531 if (mPendingCall != null) { 4532 Log.i(this, "startCallConfirmation: call %s is already pending; disconnecting %s", 4533 mPendingCall.getId(), call.getId()); 4534 markCallDisconnectedDueToSelfManagedCall(call); 4535 confirmationFuture.complete(null); 4536 return; 4537 } 4538 Log.addEvent(call, LogUtils.Events.USER_CONFIRMATION); 4539 mPendingCall = call; 4540 mPendingCallConfirm = confirmationFuture; 4541 4542 // Figure out the name of the app in charge of the self-managed call(s). 4543 Call activeCall = (Call) mConnectionSvrFocusMgr.getCurrentFocusCall(); 4544 if (activeCall != null) { 4545 CharSequence ongoingAppName = activeCall.getTargetPhoneAccountLabel(); 4546 Log.i(this, "startCallConfirmation: callId=%s, ongoingApp=%s", call.getId(), 4547 ongoingAppName); 4548 4549 Intent confirmIntent = new Intent(mContext, ConfirmCallDialogActivity.class); 4550 confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_OUTGOING_CALL_ID, call.getId()); 4551 confirmIntent.putExtra(ConfirmCallDialogActivity.EXTRA_ONGOING_APP_NAME, ongoingAppName); 4552 confirmIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 4553 mContext.startActivityAsUser(confirmIntent, UserHandle.CURRENT); 4554 } 4555 } 4556 4557 /** 4558 * Disconnects all self-managed calls. 4559 */ 4560 private void disconnectSelfManagedCalls(String reason) { 4561 // Disconnect all self-managed calls to make priority for emergency call. 4562 // Use Call.disconnect() to command the ConnectionService to disconnect the calls. 4563 // CallsManager.markCallAsDisconnected doesn't actually tell the ConnectionService to 4564 // disconnect. 4565 mCalls.stream() 4566 .filter(c -> c.isSelfManaged()) 4567 .forEach(c -> c.disconnect(reason)); 4568 4569 // When disconnecting all self-managed calls, switch audio routing back to the baseline 4570 // route. This ensures if, for example, the self-managed ConnectionService was routed to 4571 // speakerphone that we'll switch back to earpiece for the managed call which necessitated 4572 // disconnecting the self-managed calls. 4573 mCallAudioManager.switchBaseline(); 4574 } 4575 4576 /** 4577 * Dumps the state of the {@link CallsManager}. 4578 * 4579 * @param pw The {@code IndentingPrintWriter} to write the state to. 4580 */ 4581 public void dump(IndentingPrintWriter pw) { 4582 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 4583 if (mCalls != null) { 4584 pw.println("mCalls: "); 4585 pw.increaseIndent(); 4586 for (Call call : mCalls) { 4587 pw.println(call); 4588 } 4589 pw.decreaseIndent(); 4590 } 4591 4592 if (mPendingCall != null) { 4593 pw.print("mPendingCall:"); 4594 pw.println(mPendingCall.getId()); 4595 } 4596 4597 if (mPendingRedirectedOutgoingCallInfo.size() > 0) { 4598 pw.print("mPendingRedirectedOutgoingCallInfo:"); 4599 pw.println(mPendingRedirectedOutgoingCallInfo.keySet().stream().collect( 4600 Collectors.joining(", "))); 4601 } 4602 4603 if (mPendingUnredirectedOutgoingCallInfo.size() > 0) { 4604 pw.print("mPendingUnredirectedOutgoingCallInfo:"); 4605 pw.println(mPendingUnredirectedOutgoingCallInfo.keySet().stream().collect( 4606 Collectors.joining(", "))); 4607 } 4608 4609 if (mCallAudioManager != null) { 4610 pw.println("mCallAudioManager:"); 4611 pw.increaseIndent(); 4612 mCallAudioManager.dump(pw); 4613 pw.decreaseIndent(); 4614 } 4615 4616 if (mTtyManager != null) { 4617 pw.println("mTtyManager:"); 4618 pw.increaseIndent(); 4619 mTtyManager.dump(pw); 4620 pw.decreaseIndent(); 4621 } 4622 4623 if (mInCallController != null) { 4624 pw.println("mInCallController:"); 4625 pw.increaseIndent(); 4626 mInCallController.dump(pw); 4627 pw.decreaseIndent(); 4628 } 4629 4630 if (mDefaultDialerCache != null) { 4631 pw.println("mDefaultDialerCache:"); 4632 pw.increaseIndent(); 4633 mDefaultDialerCache.dumpCache(pw); 4634 pw.decreaseIndent(); 4635 } 4636 4637 if (mConnectionServiceRepository != null) { 4638 pw.println("mConnectionServiceRepository:"); 4639 pw.increaseIndent(); 4640 mConnectionServiceRepository.dump(pw); 4641 pw.decreaseIndent(); 4642 } 4643 4644 if (mRoleManagerAdapter != null && mRoleManagerAdapter instanceof RoleManagerAdapterImpl) { 4645 RoleManagerAdapterImpl impl = (RoleManagerAdapterImpl) mRoleManagerAdapter; 4646 pw.println("mRoleManager:"); 4647 pw.increaseIndent(); 4648 impl.dump(pw); 4649 pw.decreaseIndent(); 4650 } 4651 } 4652 4653 /** 4654 * For some disconnected causes, we show a dialog when it's a mmi code or potential mmi code. 4655 * 4656 * @param call The call. 4657 */ 4658 private void maybeShowErrorDialogOnDisconnect(Call call) { 4659 if (call.getState() == CallState.DISCONNECTED && (isPotentialMMICode(call.getHandle()) 4660 || isPotentialInCallMMICode(call.getHandle())) && !mCalls.contains(call)) { 4661 DisconnectCause disconnectCause = call.getDisconnectCause(); 4662 if (!TextUtils.isEmpty(disconnectCause.getDescription()) && (disconnectCause.getCode() 4663 == DisconnectCause.ERROR)) { 4664 Intent errorIntent = new Intent(mContext, ErrorDialogActivity.class); 4665 errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_STRING_EXTRA, 4666 disconnectCause.getDescription()); 4667 errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 4668 mContext.startActivityAsUser(errorIntent, UserHandle.CURRENT); 4669 } 4670 } 4671 } 4672 4673 private void setIntentExtrasAndStartTime(Call call, Bundle extras) { 4674 if (extras != null) { 4675 // Create our own instance to modify (since extras may be Bundle.EMPTY) 4676 extras = new Bundle(extras); 4677 } else { 4678 extras = new Bundle(); 4679 } 4680 4681 // Specifies the time telecom began routing the call. This is used by the dialer for 4682 // analytics. 4683 extras.putLong(TelecomManager.EXTRA_CALL_TELECOM_ROUTING_START_TIME_MILLIS, 4684 SystemClock.elapsedRealtime()); 4685 4686 call.setIntentExtras(extras); 4687 } 4688 4689 private void setCallSourceToAnalytics(Call call, Intent originalIntent) { 4690 if (originalIntent == null) { 4691 return; 4692 } 4693 4694 int callSource = originalIntent.getIntExtra(TelecomManager.EXTRA_CALL_SOURCE, 4695 Analytics.CALL_SOURCE_UNSPECIFIED); 4696 4697 // Call source is only used by metrics, so we simply set it to Analytics directly. 4698 call.getAnalytics().setCallSource(callSource); 4699 } 4700 4701 private boolean isVoicemail(Uri callHandle, PhoneAccount phoneAccount) { 4702 if (callHandle == null) { 4703 return false; 4704 } 4705 if (PhoneAccount.SCHEME_VOICEMAIL.equals(callHandle.getScheme())) { 4706 return true; 4707 } 4708 return phoneAccount != null && mPhoneAccountRegistrar.isVoiceMailNumber( 4709 phoneAccount.getAccountHandle(), 4710 callHandle.getSchemeSpecificPart()); 4711 } 4712 4713 /** 4714 * Notifies the {@link android.telecom.ConnectionService} associated with a 4715 * {@link PhoneAccountHandle} that the attempt to create a new connection has failed. 4716 * 4717 * @param phoneAccountHandle The {@link PhoneAccountHandle}. 4718 * @param call The {@link Call} which could not be added. 4719 */ 4720 private void notifyCreateConnectionFailed(PhoneAccountHandle phoneAccountHandle, Call call) { 4721 if (phoneAccountHandle == null) { 4722 return; 4723 } 4724 ConnectionServiceWrapper service = mConnectionServiceRepository.getService( 4725 phoneAccountHandle.getComponentName(), phoneAccountHandle.getUserHandle()); 4726 if (service == null) { 4727 Log.i(this, "Found no connection service."); 4728 return; 4729 } else { 4730 call.setConnectionService(service); 4731 service.createConnectionFailed(call); 4732 } 4733 } 4734 4735 /** 4736 * Notifies the {@link android.telecom.ConnectionService} associated with a 4737 * {@link PhoneAccountHandle} that the attempt to create a new connection has failed. 4738 * 4739 * @param phoneAccountHandle The {@link PhoneAccountHandle}. 4740 * @param call The {@link Call} which could not be added. 4741 */ 4742 private void notifyCreateConferenceFailed(PhoneAccountHandle phoneAccountHandle, Call call) { 4743 if (phoneAccountHandle == null) { 4744 return; 4745 } 4746 ConnectionServiceWrapper service = mConnectionServiceRepository.getService( 4747 phoneAccountHandle.getComponentName(), phoneAccountHandle.getUserHandle()); 4748 if (service == null) { 4749 Log.i(this, "Found no connection service."); 4750 return; 4751 } else { 4752 call.setConnectionService(service); 4753 service.createConferenceFailed(call); 4754 } 4755 } 4756 4757 4758 /** 4759 * Notifies the {@link android.telecom.ConnectionService} associated with a 4760 * {@link PhoneAccountHandle} that the attempt to handover a call has failed. 4761 * 4762 * @param call The handover call 4763 * @param reason The error reason code for handover failure 4764 */ 4765 private void notifyHandoverFailed(Call call, int reason) { 4766 ConnectionServiceWrapper service = call.getConnectionService(); 4767 service.handoverFailed(call, reason); 4768 call.setDisconnectCause(new DisconnectCause(DisconnectCause.CANCELED)); 4769 call.disconnect("handover failed"); 4770 } 4771 4772 /** 4773 * Called in response to a {@link Call} receiving a {@link Call#sendCallEvent(String, Bundle)} 4774 * of type {@link android.telecom.Call#EVENT_REQUEST_HANDOVER} indicating the 4775 * {@link android.telecom.InCallService} has requested a handover to another 4776 * {@link android.telecom.ConnectionService}. 4777 * 4778 * We will explicitly disallow a handover when there is an emergency call present. 4779 * 4780 * @param handoverFromCall The {@link Call} to be handed over. 4781 * @param handoverToHandle The {@link PhoneAccountHandle} to hand over the call to. 4782 * @param videoState The desired video state of {@link Call} after handover. 4783 * @param initiatingExtras Extras associated with the handover, to be passed to the handover 4784 * {@link android.telecom.ConnectionService}. 4785 */ 4786 private void requestHandoverViaEvents(Call handoverFromCall, 4787 PhoneAccountHandle handoverToHandle, 4788 int videoState, Bundle initiatingExtras) { 4789 4790 handoverFromCall.sendCallEvent(android.telecom.Call.EVENT_HANDOVER_FAILED, null); 4791 Log.addEvent(handoverFromCall, LogUtils.Events.HANDOVER_REQUEST, "legacy request denied"); 4792 } 4793 4794 /** 4795 * Called in response to a {@link Call} receiving a {@link Call#handoverTo(PhoneAccountHandle, 4796 * int, Bundle)} indicating the {@link android.telecom.InCallService} has requested a 4797 * handover to another {@link android.telecom.ConnectionService}. 4798 * 4799 * We will explicitly disallow a handover when there is an emergency call present. 4800 * 4801 * @param handoverFromCall The {@link Call} to be handed over. 4802 * @param handoverToHandle The {@link PhoneAccountHandle} to hand over the call to. 4803 * @param videoState The desired video state of {@link Call} after handover. 4804 * @param extras Extras associated with the handover, to be passed to the handover 4805 * {@link android.telecom.ConnectionService}. 4806 */ 4807 private void requestHandover(Call handoverFromCall, PhoneAccountHandle handoverToHandle, 4808 int videoState, Bundle extras) { 4809 4810 // Send an error back if there are any ongoing emergency calls. 4811 if (isInEmergencyCall()) { 4812 handoverFromCall.onHandoverFailed( 4813 android.telecom.Call.Callback.HANDOVER_FAILURE_ONGOING_EMERGENCY_CALL); 4814 return; 4815 } 4816 4817 // If source and destination phone accounts don't support handover, send an error back. 4818 boolean isHandoverFromSupported = isHandoverFromPhoneAccountSupported( 4819 handoverFromCall.getTargetPhoneAccount()); 4820 boolean isHandoverToSupported = isHandoverToPhoneAccountSupported(handoverToHandle); 4821 if (!isHandoverFromSupported || !isHandoverToSupported) { 4822 handoverFromCall.onHandoverFailed( 4823 android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED); 4824 return; 4825 } 4826 4827 Log.addEvent(handoverFromCall, LogUtils.Events.HANDOVER_REQUEST, handoverToHandle); 4828 4829 // Create a new instance of Call 4830 PhoneAccount account = 4831 mPhoneAccountRegistrar.getPhoneAccount(handoverToHandle, getCurrentUserHandle()); 4832 boolean isSelfManaged = account != null && account.isSelfManaged(); 4833 4834 Call call = new Call(getNextCallId(), mContext, 4835 this, mLock, mConnectionServiceRepository, 4836 mPhoneNumberUtilsAdapter, 4837 handoverFromCall.getHandle(), null, 4838 null, null, 4839 Call.CALL_DIRECTION_OUTGOING, false, 4840 false, mClockProxy, mToastFactory); 4841 call.initAnalytics(); 4842 4843 // Set self-managed and voipAudioMode if destination is self-managed CS 4844 call.setIsSelfManaged(isSelfManaged); 4845 if (isSelfManaged) { 4846 call.setIsVoipAudioMode(true); 4847 } 4848 call.setInitiatingUser(getCurrentUserHandle()); 4849 4850 // Ensure we don't try to place an outgoing call with video if video is not 4851 // supported. 4852 if (VideoProfile.isVideo(videoState) && account != null && 4853 !account.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) { 4854 call.setVideoState(VideoProfile.STATE_AUDIO_ONLY); 4855 } else { 4856 call.setVideoState(videoState); 4857 } 4858 4859 // Set target phone account to destAcct. 4860 call.setTargetPhoneAccount(handoverToHandle); 4861 4862 if (account != null && account.getExtras() != null && account.getExtras() 4863 .getBoolean(PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE)) { 4864 Log.d(this, "requestHandover: defaulting to voip mode for call %s", 4865 call.getId()); 4866 call.setIsVoipAudioMode(true); 4867 } 4868 4869 // Set call state to connecting 4870 call.setState( 4871 CallState.CONNECTING, 4872 handoverToHandle == null ? "no-handle" : handoverToHandle.toString()); 4873 4874 // Mark as handover so that the ConnectionService knows this is a handover request. 4875 if (extras == null) { 4876 extras = new Bundle(); 4877 } 4878 extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true); 4879 extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, 4880 handoverFromCall.getTargetPhoneAccount()); 4881 setIntentExtrasAndStartTime(call, extras); 4882 4883 // Add call to call tracker 4884 if (!mCalls.contains(call)) { 4885 addCall(call); 4886 } 4887 4888 Log.addEvent(handoverFromCall, LogUtils.Events.START_HANDOVER, 4889 "handOverFrom=%s, handOverTo=%s", handoverFromCall.getId(), call.getId()); 4890 4891 handoverFromCall.setHandoverDestinationCall(call); 4892 handoverFromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); 4893 call.setHandoverState(HandoverState.HANDOVER_TO_STARTED); 4894 call.setHandoverSourceCall(handoverFromCall); 4895 call.setNewOutgoingCallIntentBroadcastIsDone(); 4896 4897 // Auto-enable speakerphone if the originating intent specified to do so, if the call 4898 // is a video call, of if using speaker when docked 4899 final boolean useSpeakerWhenDocked = mContext.getResources().getBoolean( 4900 R.bool.use_speaker_when_docked); 4901 final boolean useSpeakerForDock = isSpeakerphoneEnabledForDock(); 4902 final boolean useSpeakerForVideoCall = isSpeakerphoneAutoEnabledForVideoCalls(videoState); 4903 call.setStartWithSpeakerphoneOn(false || useSpeakerForVideoCall 4904 || (useSpeakerWhenDocked && useSpeakerForDock)); 4905 call.setVideoState(videoState); 4906 4907 final boolean isOutgoingCallPermitted = isOutgoingCallPermitted(call, 4908 call.getTargetPhoneAccount()); 4909 4910 // If the account has been set, proceed to place the outgoing call. 4911 if (call.isSelfManaged() && !isOutgoingCallPermitted) { 4912 notifyCreateConnectionFailed(call.getTargetPhoneAccount(), call); 4913 } else if (!call.isSelfManaged() && hasSelfManagedCalls() && !call.isEmergencyCall()) { 4914 markCallDisconnectedDueToSelfManagedCall(call); 4915 } else { 4916 if (call.isEmergencyCall()) { 4917 // Disconnect all self-managed calls to make priority for emergency call. 4918 disconnectSelfManagedCalls("emergency call"); 4919 } 4920 4921 call.startCreateConnection(mPhoneAccountRegistrar); 4922 } 4923 4924 } 4925 4926 /** 4927 * Determines if handover from the specified {@link PhoneAccountHandle} is supported. 4928 * 4929 * @param from The {@link PhoneAccountHandle} the handover originates from. 4930 * @return {@code true} if handover is currently allowed, {@code false} otherwise. 4931 */ 4932 private boolean isHandoverFromPhoneAccountSupported(PhoneAccountHandle from) { 4933 return getBooleanPhoneAccountExtra(from, PhoneAccount.EXTRA_SUPPORTS_HANDOVER_FROM); 4934 } 4935 4936 /** 4937 * Determines if handover to the specified {@link PhoneAccountHandle} is supported. 4938 * 4939 * @param to The {@link PhoneAccountHandle} the handover it to. 4940 * @return {@code true} if handover is currently allowed, {@code false} otherwise. 4941 */ 4942 private boolean isHandoverToPhoneAccountSupported(PhoneAccountHandle to) { 4943 return getBooleanPhoneAccountExtra(to, PhoneAccount.EXTRA_SUPPORTS_HANDOVER_TO); 4944 } 4945 4946 /** 4947 * Retrieves a boolean phone account extra. 4948 * @param handle the {@link PhoneAccountHandle} to retrieve the extra for. 4949 * @param key The extras key. 4950 * @return {@code true} if the extra {@link PhoneAccount} extra is true, {@code false} 4951 * otherwise. 4952 */ 4953 private boolean getBooleanPhoneAccountExtra(PhoneAccountHandle handle, String key) { 4954 PhoneAccount phoneAccount = getPhoneAccountRegistrar().getPhoneAccountUnchecked(handle); 4955 if (phoneAccount == null) { 4956 return false; 4957 } 4958 4959 Bundle fromExtras = phoneAccount.getExtras(); 4960 if (fromExtras == null) { 4961 return false; 4962 } 4963 return fromExtras.getBoolean(key); 4964 } 4965 4966 /** 4967 * Determines if there is an existing handover in process. 4968 * @return {@code true} if a call in the process of handover exists, {@code false} otherwise. 4969 */ 4970 private boolean isHandoverInProgress() { 4971 return mCalls.stream().filter(c -> c.getHandoverSourceCall() != null || 4972 c.getHandoverDestinationCall() != null).count() > 0; 4973 } 4974 4975 private void broadcastUnregisterIntent(PhoneAccountHandle accountHandle) { 4976 Intent intent = 4977 new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED); 4978 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 4979 intent.putExtra( 4980 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle); 4981 Log.i(this, "Sending phone-account %s unregistered intent as user", accountHandle); 4982 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 4983 PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION); 4984 4985 String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication( 4986 getCurrentUserHandle().getIdentifier()); 4987 if (!TextUtils.isEmpty(dialerPackage)) { 4988 Intent directedIntent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_UNREGISTERED) 4989 .setPackage(dialerPackage); 4990 directedIntent.putExtra( 4991 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle); 4992 Log.i(this, "Sending phone-account unregistered intent to default dialer"); 4993 mContext.sendBroadcastAsUser(directedIntent, UserHandle.ALL, null); 4994 } 4995 return ; 4996 } 4997 4998 private void broadcastRegisterIntent(PhoneAccountHandle accountHandle) { 4999 Intent intent = new Intent( 5000 TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED); 5001 intent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 5002 intent.putExtra(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, 5003 accountHandle); 5004 Log.i(this, "Sending phone-account %s registered intent as user", accountHandle); 5005 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 5006 PERMISSION_PROCESS_PHONE_ACCOUNT_REGISTRATION); 5007 5008 String dialerPackage = mDefaultDialerCache.getDefaultDialerApplication( 5009 getCurrentUserHandle().getIdentifier()); 5010 if (!TextUtils.isEmpty(dialerPackage)) { 5011 Intent directedIntent = new Intent(TelecomManager.ACTION_PHONE_ACCOUNT_REGISTERED) 5012 .setPackage(dialerPackage); 5013 directedIntent.putExtra( 5014 TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, accountHandle); 5015 Log.i(this, "Sending phone-account registered intent to default dialer"); 5016 mContext.sendBroadcastAsUser(directedIntent, UserHandle.ALL, null); 5017 } 5018 return ; 5019 } 5020 5021 public void acceptHandover(Uri srcAddr, int videoState, PhoneAccountHandle destAcct) { 5022 final String handleScheme = srcAddr.getSchemeSpecificPart(); 5023 Call fromCall = mCalls.stream() 5024 .filter((c) -> mPhoneNumberUtilsAdapter.isSamePhoneNumber( 5025 (c.getHandle() == null ? null : c.getHandle().getSchemeSpecificPart()), 5026 handleScheme)) 5027 .findFirst() 5028 .orElse(null); 5029 5030 Call call = new Call( 5031 getNextCallId(), 5032 mContext, 5033 this, 5034 mLock, 5035 mConnectionServiceRepository, 5036 mPhoneNumberUtilsAdapter, 5037 srcAddr, 5038 null /* gatewayInfo */, 5039 null /* connectionManagerPhoneAccount */, 5040 destAcct, 5041 Call.CALL_DIRECTION_INCOMING /* callDirection */, 5042 false /* forceAttachToExistingConnection */, 5043 false, /* isConference */ 5044 mClockProxy, 5045 mToastFactory); 5046 5047 if (fromCall == null || isHandoverInProgress() || 5048 !isHandoverFromPhoneAccountSupported(fromCall.getTargetPhoneAccount()) || 5049 !isHandoverToPhoneAccountSupported(destAcct) || 5050 isInEmergencyCall()) { 5051 Log.w(this, "acceptHandover: Handover not supported"); 5052 notifyHandoverFailed(call, 5053 android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED); 5054 return; 5055 } 5056 5057 PhoneAccount phoneAccount = mPhoneAccountRegistrar.getPhoneAccountUnchecked(destAcct); 5058 if (phoneAccount == null) { 5059 Log.w(this, "acceptHandover: Handover not supported. phoneAccount = null"); 5060 notifyHandoverFailed(call, 5061 android.telecom.Call.Callback.HANDOVER_FAILURE_NOT_SUPPORTED); 5062 return; 5063 } 5064 call.setIsSelfManaged(phoneAccount.isSelfManaged()); 5065 if (call.isSelfManaged() || (phoneAccount.getExtras() != null && 5066 phoneAccount.getExtras().getBoolean( 5067 PhoneAccount.EXTRA_ALWAYS_USE_VOIP_AUDIO_MODE))) { 5068 call.setIsVoipAudioMode(true); 5069 } 5070 if (!phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING)) { 5071 call.setVideoState(VideoProfile.STATE_AUDIO_ONLY); 5072 } else { 5073 call.setVideoState(videoState); 5074 } 5075 5076 call.initAnalytics(); 5077 call.addListener(this); 5078 5079 fromCall.setHandoverDestinationCall(call); 5080 call.setHandoverSourceCall(fromCall); 5081 call.setHandoverState(HandoverState.HANDOVER_TO_STARTED); 5082 fromCall.setHandoverState(HandoverState.HANDOVER_FROM_STARTED); 5083 5084 if (isSpeakerEnabledForVideoCalls() && VideoProfile.isVideo(videoState)) { 5085 // Ensure when the call goes active that it will go to speakerphone if the 5086 // handover to call is a video call. 5087 call.setStartWithSpeakerphoneOn(true); 5088 } 5089 5090 Bundle extras = call.getIntentExtras(); 5091 if (extras == null) { 5092 extras = new Bundle(); 5093 } 5094 extras.putBoolean(TelecomManager.EXTRA_IS_HANDOVER_CONNECTION, true); 5095 extras.putParcelable(TelecomManager.EXTRA_HANDOVER_FROM_PHONE_ACCOUNT, 5096 fromCall.getTargetPhoneAccount()); 5097 5098 call.startCreateConnection(mPhoneAccountRegistrar); 5099 } 5100 5101 public ConnectionServiceFocusManager getConnectionServiceFocusManager() { 5102 return mConnectionSvrFocusMgr; 5103 } 5104 5105 private boolean canHold(Call call) { 5106 return call.can(Connection.CAPABILITY_HOLD) && call.getState() != CallState.DIALING; 5107 } 5108 5109 private boolean supportsHold(Call call) { 5110 return call.can(Connection.CAPABILITY_SUPPORT_HOLD); 5111 } 5112 5113 private final class ActionSetCallState implements PendingAction { 5114 5115 private final Call mCall; 5116 private final int mState; 5117 private final String mTag; 5118 5119 ActionSetCallState(Call call, int state, String tag) { 5120 mCall = call; 5121 mState = state; 5122 mTag = tag; 5123 } 5124 5125 @Override 5126 public void performAction() { 5127 synchronized (mLock) { 5128 Log.d(this, "perform set call state for %s, state = %s", mCall, mState); 5129 setCallState(mCall, mState, mTag); 5130 } 5131 } 5132 } 5133 5134 private final class ActionUnHoldCall implements PendingAction { 5135 private final Call mCall; 5136 private final String mPreviouslyHeldCallId; 5137 5138 ActionUnHoldCall(Call call, String previouslyHeldCallId) { 5139 mCall = call; 5140 mPreviouslyHeldCallId = previouslyHeldCallId; 5141 } 5142 5143 @Override 5144 public void performAction() { 5145 synchronized (mLock) { 5146 Log.d(this, "perform unhold call for %s", mCall); 5147 mCall.unhold("held " + mPreviouslyHeldCallId); 5148 } 5149 } 5150 } 5151 5152 private final class ActionAnswerCall implements PendingAction { 5153 private final Call mCall; 5154 private final int mVideoState; 5155 5156 ActionAnswerCall(Call call, int videoState) { 5157 mCall = call; 5158 mVideoState = videoState; 5159 } 5160 5161 @Override 5162 public void performAction() { 5163 synchronized (mLock) { 5164 Log.d(this, "perform answer call for %s, videoState = %d", mCall, mVideoState); 5165 for (CallsManagerListener listener : mListeners) { 5166 listener.onIncomingCallAnswered(mCall); 5167 } 5168 5169 // We do not update the UI until we get confirmation of the answer() through 5170 // {@link #markCallAsActive}. 5171 if (mCall.getState() == CallState.RINGING) { 5172 mCall.answer(mVideoState); 5173 setCallState(mCall, CallState.ANSWERED, "answered"); 5174 } else if (mCall.getState() == CallState.SIMULATED_RINGING) { 5175 // If the call's in simulated ringing, we don't have to wait for the CS -- 5176 // we can just declare it active. 5177 setCallState(mCall, CallState.ACTIVE, "answering simulated ringing"); 5178 Log.addEvent(mCall, LogUtils.Events.REQUEST_SIMULATED_ACCEPT); 5179 } else if (mCall.getState() == CallState.ANSWERED) { 5180 // In certain circumstances, the connection service can lose track of a request 5181 // to answer a call. Therefore, if the user presses answer again, still send it 5182 // on down, but log a warning in the process and don't change the call state. 5183 mCall.answer(mVideoState); 5184 Log.w(this, "Duplicate answer request for call %s", mCall.getId()); 5185 } 5186 if (isSpeakerphoneAutoEnabledForVideoCalls(mVideoState)) { 5187 mCall.setStartWithSpeakerphoneOn(true); 5188 } 5189 } 5190 } 5191 } 5192 5193 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 5194 public static final class RequestCallback implements 5195 ConnectionServiceFocusManager.RequestFocusCallback { 5196 private PendingAction mPendingAction; 5197 5198 RequestCallback(PendingAction pendingAction) { 5199 mPendingAction = pendingAction; 5200 } 5201 5202 @Override 5203 public void onRequestFocusDone(ConnectionServiceFocusManager.CallFocus call) { 5204 if (mPendingAction != null) { 5205 mPendingAction.performAction(); 5206 } 5207 } 5208 } 5209 5210 public void resetConnectionTime(Call call) { 5211 call.setConnectTimeMillis(System.currentTimeMillis()); 5212 call.setConnectElapsedTimeMillis(SystemClock.elapsedRealtime()); 5213 if (mCalls.contains(call)) { 5214 for (CallsManagerListener listener : mListeners) { 5215 listener.onConnectionTimeChanged(call); 5216 } 5217 } 5218 } 5219 5220 public Context getContext() { 5221 return mContext; 5222 } 5223 5224 /** 5225 * Determines if there is an ongoing emergency call. This can be either an outgoing emergency 5226 * call, or a number which has been identified by the number as an emergency call. 5227 * @return {@code true} if there is an ongoing emergency call, {@code false} otherwise. 5228 */ 5229 public boolean isInEmergencyCall() { 5230 return mCalls.stream().filter(c -> (c.isEmergencyCall() 5231 || c.isNetworkIdentifiedEmergencyCall()) && !c.isDisconnected()).count() > 0; 5232 } 5233 5234 /** 5235 * Trigger a recalculation of support for CAPABILITY_CAN_PULL_CALL for external calls due to 5236 * a possible emergency call being added/removed. 5237 */ 5238 private void updateExternalCallCanPullSupport() { 5239 boolean isInEmergencyCall = isInEmergencyCall(); 5240 // Remove the capability to pull an external call in the case that we are in an emergency 5241 // call. 5242 mCalls.stream().filter(Call::isExternalCall).forEach( 5243 c->c.setIsPullExternalCallSupported(!isInEmergencyCall)); 5244 } 5245 5246 /** 5247 * Trigger display of an error message to the user; we do this outside of dialer for calls which 5248 * fail to be created and added to Dialer. 5249 * @param messageId The string resource id. 5250 */ 5251 private void showErrorMessage(int messageId) { 5252 final Intent errorIntent = new Intent(mContext, ErrorDialogActivity.class); 5253 errorIntent.putExtra(ErrorDialogActivity.ERROR_MESSAGE_ID_EXTRA, messageId); 5254 errorIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 5255 mContext.startActivityAsUser(errorIntent, UserHandle.CURRENT); 5256 } 5257 5258 /** 5259 * Handles changes to a {@link PhoneAccount}. 5260 * 5261 * Checks for changes to video calling availability and updates whether calls for that phone 5262 * account are video capable. 5263 * 5264 * @param registrar The {@link PhoneAccountRegistrar} originating the change. 5265 * @param phoneAccount The {@link PhoneAccount} which changed. 5266 */ 5267 private void handlePhoneAccountChanged(PhoneAccountRegistrar registrar, 5268 PhoneAccount phoneAccount) { 5269 Log.i(this, "handlePhoneAccountChanged: phoneAccount=%s", phoneAccount); 5270 boolean isVideoNowSupported = phoneAccount.hasCapabilities( 5271 PhoneAccount.CAPABILITY_VIDEO_CALLING); 5272 mCalls.stream() 5273 .filter(c -> phoneAccount.getAccountHandle().equals(c.getTargetPhoneAccount())) 5274 .forEach(c -> c.setVideoCallingSupportedByPhoneAccount(isVideoNowSupported)); 5275 } 5276 5277 /** 5278 * Determines if two {@link Call} instances originated from either the same target 5279 * {@link PhoneAccountHandle} or connection manager {@link PhoneAccountHandle}. 5280 * @param call1 The first call 5281 * @param call2 The second call 5282 * @return {@code true} if both calls are from the same target or connection manager 5283 * {@link PhoneAccountHandle}. 5284 */ 5285 public static boolean areFromSameSource(@NonNull Call call1, @NonNull Call call2) { 5286 PhoneAccountHandle call1ConnectionMgr = call1.getConnectionManagerPhoneAccount(); 5287 PhoneAccountHandle call2ConnectionMgr = call2.getConnectionManagerPhoneAccount(); 5288 5289 if (call1ConnectionMgr != null && call2ConnectionMgr != null 5290 && PhoneAccountHandle.areFromSamePackage(call1ConnectionMgr, call2ConnectionMgr)) { 5291 // Both calls share the same connection manager package, so they are from the same 5292 // source. 5293 return true; 5294 } 5295 5296 PhoneAccountHandle call1TargetAcct = call1.getTargetPhoneAccount(); 5297 PhoneAccountHandle call2TargetAcct = call2.getTargetPhoneAccount(); 5298 // Otherwise if the target phone account for both is the same package, they're the same 5299 // source. 5300 return PhoneAccountHandle.areFromSamePackage(call1TargetAcct, call2TargetAcct); 5301 } 5302 5303 public LinkedList<HandlerThread> getGraphHandlerThreads() { 5304 return mGraphHandlerThreads; 5305 } 5306 5307 private void maybeSendPostCallScreenIntent(Call call) { 5308 if (call.isEmergencyCall() || (call.isNetworkIdentifiedEmergencyCall()) || 5309 (call.getPostCallPackageName() == null)) { 5310 return; 5311 } 5312 5313 Intent intent = new Intent(ACTION_POST_CALL); 5314 intent.setPackage(call.getPostCallPackageName()); 5315 intent.putExtra(EXTRA_HANDLE, call.getHandle()); 5316 intent.putExtra(EXTRA_DISCONNECT_CAUSE, call.getDisconnectCause().getCode()); 5317 long duration = call.getAgeMillis(); 5318 int durationCode = DURATION_VERY_SHORT; 5319 if ((duration >= VERY_SHORT_CALL_TIME_MS) && (duration < SHORT_CALL_TIME_MS)) { 5320 durationCode = DURATION_SHORT; 5321 } else if ((duration >= SHORT_CALL_TIME_MS) && (duration < MEDIUM_CALL_TIME_MS)) { 5322 durationCode = DURATION_MEDIUM; 5323 } else if (duration >= MEDIUM_CALL_TIME_MS) { 5324 durationCode = DURATION_LONG; 5325 } 5326 intent.putExtra(EXTRA_CALL_DURATION, durationCode); 5327 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 5328 mContext.startActivityAsUser(intent, mCurrentUserHandle); 5329 } 5330 } 5331