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