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