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