1 /* 2 * Copyright (C) 2014 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.telephony.TelephonyManager.EVENT_DISPLAY_EMERGENCY_MESSAGE; 21 22 import static com.android.server.telecom.CachedCallback.TYPE_QUEUE; 23 import static com.android.server.telecom.CachedCallback.TYPE_STATE; 24 import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation 25 .TransactionalVideoStateToString; 26 import static com.android.server.telecom.callsequencing.voip.VideoStateTranslation 27 .VideoProfileStateToTransactionalVideoState; 28 29 import android.annotation.NonNull; 30 import android.annotation.Nullable; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.pm.PackageManager; 34 import android.graphics.Bitmap; 35 import android.graphics.drawable.Drawable; 36 import android.net.Uri; 37 import android.os.Bundle; 38 import android.os.Handler; 39 import android.os.Looper; 40 import android.os.OutcomeReceiver; 41 import android.os.ParcelFileDescriptor; 42 import android.os.RemoteException; 43 import android.os.SystemClock; 44 import android.os.UserHandle; 45 import android.provider.CallLog; 46 import android.provider.ContactsContract.Contacts; 47 import android.telecom.BluetoothCallQualityReport; 48 import android.telecom.CallAttributes; 49 import android.telecom.CallAudioState; 50 import android.telecom.CallDiagnosticService; 51 import android.telecom.CallDiagnostics; 52 import android.telecom.CallException; 53 import android.telecom.CallerInfo; 54 import android.telecom.Conference; 55 import android.telecom.Connection; 56 import android.telecom.ConnectionService; 57 import android.telecom.DisconnectCause; 58 import android.telecom.GatewayInfo; 59 import android.telecom.Log; 60 import android.telecom.Logging.EventManager; 61 import android.telecom.ParcelableConference; 62 import android.telecom.ParcelableConnection; 63 import android.telecom.PhoneAccount; 64 import android.telecom.PhoneAccountHandle; 65 import android.telecom.StatusHints; 66 import android.telecom.TelecomManager; 67 import android.telecom.VideoProfile; 68 import android.telephony.CallQuality; 69 import android.telephony.PhoneNumberUtils; 70 import android.telephony.TelephonyManager; 71 import android.telephony.emergency.EmergencyNumber; 72 import android.telephony.ims.ImsReasonInfo; 73 import android.text.TextUtils; 74 import android.widget.Toast; 75 76 import com.android.internal.annotations.VisibleForTesting; 77 import com.android.internal.telecom.IVideoProvider; 78 import com.android.internal.util.Preconditions; 79 import com.android.server.telecom.flags.FeatureFlags; 80 import com.android.server.telecom.stats.CallFailureCause; 81 import com.android.server.telecom.stats.CallStateChangedAtomWriter; 82 import com.android.server.telecom.ui.ToastFactory; 83 import com.android.server.telecom.callsequencing.CallTransaction; 84 import com.android.server.telecom.callsequencing.TransactionManager; 85 import com.android.server.telecom.callsequencing.VerifyCallStateChangeTransaction; 86 import com.android.server.telecom.callsequencing.CallTransactionResult; 87 88 import java.io.IOException; 89 import java.text.SimpleDateFormat; 90 import java.util.ArrayList; 91 import java.util.Collection; 92 import java.util.Collections; 93 import java.util.Date; 94 import java.util.HashMap; 95 import java.util.LinkedList; 96 import java.util.List; 97 import java.util.Locale; 98 import java.util.Map; 99 import java.util.Objects; 100 import java.util.Set; 101 import java.util.concurrent.CompletableFuture; 102 import java.util.concurrent.ConcurrentHashMap; 103 import java.util.concurrent.ExecutionException; 104 import java.util.concurrent.TimeUnit; 105 import java.util.stream.Collectors; 106 107 /** 108 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting 109 * from the time the call intent was received by Telecom (vs. the time the call was 110 * connected etc). 111 */ 112 public class Call implements CreateConnectionResponse, EventManager.Loggable, 113 ConnectionServiceFocusManager.CallFocus { 114 public final static String CALL_ID_UNKNOWN = "-1"; 115 public final static long DATA_USAGE_NOT_SET = -1; 116 117 public static final int CALL_DIRECTION_UNDEFINED = 0; 118 public static final int CALL_DIRECTION_OUTGOING = 1; 119 public static final int CALL_DIRECTION_INCOMING = 2; 120 public static final int CALL_DIRECTION_UNKNOWN = 3; 121 122 /** Identifies extras changes which originated from a connection service. */ 123 public static final int SOURCE_CONNECTION_SERVICE = 1; 124 /** Identifies extras changes which originated from an incall service. */ 125 public static final int SOURCE_INCALL_SERVICE = 2; 126 127 private static final int RTT_PIPE_READ_SIDE_INDEX = 0; 128 private static final int RTT_PIPE_WRITE_SIDE_INDEX = 1; 129 130 private static final int INVALID_RTT_REQUEST_ID = -1; 131 132 private static final char NO_DTMF_TONE = '\0'; 133 134 /** 135 * The following simultaneous call types will be set on each call on creation and may be updated 136 * according to priority level. CALL_DIRECTION_DUAL_DIFF_ACCOUNT holds the highest priority. 137 * So if for example, a call is created with CALL_DIRECTION_DUAL_SAME_ACCOUNT, it can be 138 * upgraded to CALL_DIRECTION_DUAL_DIFF_ACCOUNT if another call is added with a different phone 139 * account. 140 */ 141 public static final int CALL_SIMULTANEOUS_UNKNOWN = 0; 142 // Only used if simultaneous calling is not available 143 public static final int CALL_SIMULTANEOUS_DISABLED_SAME_ACCOUNT = 1; 144 // Only used if simultaneous calling is not available 145 public static final int CALL_SIMULTANEOUS_DISABLED_DIFF_ACCOUNT = 2; 146 public static final int CALL_DIRECTION_DUAL_SAME_ACCOUNT = 3; 147 public static final int CALL_DIRECTION_DUAL_DIFF_ACCOUNT = 4; 148 149 /** 150 * Listener for CallState changes which can be leveraged by a Transaction. 151 */ 152 public interface CallStateListener { onCallStateChanged(int newCallState)153 void onCallStateChanged(int newCallState); 154 } 155 156 public List<CallStateListener> mCallStateListeners = new ArrayList<>(); 157 addCallStateListener(CallStateListener newListener)158 public void addCallStateListener(CallStateListener newListener) { 159 mCallStateListeners.add(newListener); 160 } 161 removeCallStateListener(CallStateListener newListener)162 public boolean removeCallStateListener(CallStateListener newListener) { 163 return mCallStateListeners.remove(newListener); 164 } 165 166 /** 167 * Listener for events on the call. 168 */ 169 public interface Listener { onSuccessfulOutgoingCall(Call call, int callState)170 default void onSuccessfulOutgoingCall(Call call, int callState) {}; onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)171 default void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {}; onSuccessfulIncomingCall(Call call)172 default void onSuccessfulIncomingCall(Call call) {}; onFailedIncomingCall(Call call)173 default void onFailedIncomingCall(Call call) {}; onSuccessfulUnknownCall(Call call, int callState)174 default void onSuccessfulUnknownCall(Call call, int callState) {}; onFailedUnknownCall(Call call)175 default void onFailedUnknownCall(Call call) {}; onRingbackRequested(Call call, boolean ringbackRequested)176 default void onRingbackRequested(Call call, boolean ringbackRequested) {}; onPostDialWait(Call call, String remaining)177 default void onPostDialWait(Call call, String remaining) {}; onPostDialChar(Call call, char nextChar)178 default void onPostDialChar(Call call, char nextChar) {}; onConnectionCapabilitiesChanged(Call call)179 default void onConnectionCapabilitiesChanged(Call call) {}; onConnectionPropertiesChanged(Call call, boolean didRttChange)180 default void onConnectionPropertiesChanged(Call call, boolean didRttChange) {}; onParentChanged(Call call)181 default void onParentChanged(Call call) {}; onChildrenChanged(Call call)182 default void onChildrenChanged(Call call) {}; onCannedSmsResponsesLoaded(Call call)183 default void onCannedSmsResponsesLoaded(Call call) {}; onVideoCallProviderChanged(Call call)184 default void onVideoCallProviderChanged(Call call) {}; onCallerInfoChanged(Call call)185 default void onCallerInfoChanged(Call call) {}; onIsVoipAudioModeChanged(Call call)186 default void onIsVoipAudioModeChanged(Call call) {}; onStatusHintsChanged(Call call)187 default void onStatusHintsChanged(Call call) {}; onExtrasChanged(Call c, int source, Bundle extras, String requestingPackageName)188 default void onExtrasChanged(Call c, int source, Bundle extras, 189 String requestingPackageName) {}; onExtrasRemoved(Call c, int source, List<String> keys)190 default void onExtrasRemoved(Call c, int source, List<String> keys) {}; onHandleChanged(Call call)191 default void onHandleChanged(Call call) {}; onCallerDisplayNameChanged(Call call)192 default void onCallerDisplayNameChanged(Call call) {}; onCallDirectionChanged(Call call)193 default void onCallDirectionChanged(Call call) {}; onVideoStateChanged(Call call, int previousVideoState, int newVideoState)194 default void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {}; onTargetPhoneAccountChanged(Call call)195 default void onTargetPhoneAccountChanged(Call call) {}; onConnectionManagerPhoneAccountChanged(Call call)196 default void onConnectionManagerPhoneAccountChanged(Call call) {}; onPhoneAccountChanged(Call call)197 default void onPhoneAccountChanged(Call call) {}; onConferenceableCallsChanged(Call call)198 default void onConferenceableCallsChanged(Call call) {}; onConferenceStateChanged(Call call, boolean isConference)199 default void onConferenceStateChanged(Call call, boolean isConference) {}; onCdmaConferenceSwap(Call call)200 default void onCdmaConferenceSwap(Call call) {}; onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)201 default boolean onCanceledViaNewOutgoingCallBroadcast(Call call, 202 long disconnectionTimeout) { 203 return false; 204 }; onHoldToneRequested(Call call)205 default void onHoldToneRequested(Call call) {}; onCallHoldFailed(Call call)206 default void onCallHoldFailed(Call call) {}; onCallSwitchFailed(Call call)207 default void onCallSwitchFailed(Call call) {}; onCallResumeFailed(Call call)208 default void onCallResumeFailed(Call call) {}; onConnectionEvent(Call call, String event, Bundle extras)209 default void onConnectionEvent(Call call, String event, Bundle extras) {}; onCallStreamingStateChanged(Call call, boolean isStreaming)210 default void onCallStreamingStateChanged(Call call, boolean isStreaming) {} onExternalCallChanged(Call call, boolean isExternalCall)211 default void onExternalCallChanged(Call call, boolean isExternalCall) {}; onRttInitiationFailure(Call call, int reason)212 default void onRttInitiationFailure(Call call, int reason) {}; onRemoteRttRequest(Call call, int requestId)213 default void onRemoteRttRequest(Call call, int requestId) {}; onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy)214 default void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, 215 Bundle extras, boolean isLegacy) {}; onHandoverFailed(Call call, int error)216 default void onHandoverFailed(Call call, int error) {}; onHandoverComplete(Call call)217 default void onHandoverComplete(Call call) {}; onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report)218 default void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) {}; onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue)219 default void onReceivedDeviceToDeviceMessage(Call call, int messageType, 220 int messageValue) {}; onReceivedCallQualityReport(Call call, CallQuality callQuality)221 default void onReceivedCallQualityReport(Call call, CallQuality callQuality) {}; onCallerNumberVerificationStatusChanged(Call call, int callerNumberVerificationStatus)222 default void onCallerNumberVerificationStatusChanged(Call call, 223 int callerNumberVerificationStatus) {}; 224 } 225 226 public abstract static class ListenerBase implements Listener { 227 @Override onSuccessfulOutgoingCall(Call call, int callState)228 public void onSuccessfulOutgoingCall(Call call, int callState) {} 229 @Override onFailedOutgoingCall(Call call, DisconnectCause disconnectCause)230 public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {} 231 @Override onSuccessfulIncomingCall(Call call)232 public void onSuccessfulIncomingCall(Call call) {} 233 @Override onFailedIncomingCall(Call call)234 public void onFailedIncomingCall(Call call) {} 235 @Override onSuccessfulUnknownCall(Call call, int callState)236 public void onSuccessfulUnknownCall(Call call, int callState) {} 237 @Override onFailedUnknownCall(Call call)238 public void onFailedUnknownCall(Call call) {} 239 @Override onRingbackRequested(Call call, boolean ringbackRequested)240 public void onRingbackRequested(Call call, boolean ringbackRequested) {} 241 @Override onPostDialWait(Call call, String remaining)242 public void onPostDialWait(Call call, String remaining) {} 243 @Override onPostDialChar(Call call, char nextChar)244 public void onPostDialChar(Call call, char nextChar) {} 245 @Override onConnectionCapabilitiesChanged(Call call)246 public void onConnectionCapabilitiesChanged(Call call) {} 247 @Override onConnectionPropertiesChanged(Call call, boolean didRttChange)248 public void onConnectionPropertiesChanged(Call call, boolean didRttChange) {} 249 @Override onParentChanged(Call call)250 public void onParentChanged(Call call) {} 251 @Override onChildrenChanged(Call call)252 public void onChildrenChanged(Call call) {} 253 @Override onCannedSmsResponsesLoaded(Call call)254 public void onCannedSmsResponsesLoaded(Call call) {} 255 @Override onVideoCallProviderChanged(Call call)256 public void onVideoCallProviderChanged(Call call) {} 257 @Override onCallerInfoChanged(Call call)258 public void onCallerInfoChanged(Call call) {} 259 @Override onIsVoipAudioModeChanged(Call call)260 public void onIsVoipAudioModeChanged(Call call) {} 261 @Override onStatusHintsChanged(Call call)262 public void onStatusHintsChanged(Call call) {} 263 @Override onExtrasChanged(Call c, int source, Bundle extras, String requestingPackageName)264 public void onExtrasChanged(Call c, int source, Bundle extras, 265 String requestingPackageName) {} 266 @Override onExtrasRemoved(Call c, int source, List<String> keys)267 public void onExtrasRemoved(Call c, int source, List<String> keys) {} 268 @Override onHandleChanged(Call call)269 public void onHandleChanged(Call call) {} 270 @Override onCallerDisplayNameChanged(Call call)271 public void onCallerDisplayNameChanged(Call call) {} 272 @Override onCallDirectionChanged(Call call)273 public void onCallDirectionChanged(Call call) {} 274 @Override onVideoStateChanged(Call call, int previousVideoState, int newVideoState)275 public void onVideoStateChanged(Call call, int previousVideoState, int newVideoState) {} 276 @Override onTargetPhoneAccountChanged(Call call)277 public void onTargetPhoneAccountChanged(Call call) {} 278 @Override onConnectionManagerPhoneAccountChanged(Call call)279 public void onConnectionManagerPhoneAccountChanged(Call call) {} 280 @Override onPhoneAccountChanged(Call call)281 public void onPhoneAccountChanged(Call call) {} 282 @Override onConferenceableCallsChanged(Call call)283 public void onConferenceableCallsChanged(Call call) {} 284 @Override onConferenceStateChanged(Call call, boolean isConference)285 public void onConferenceStateChanged(Call call, boolean isConference) {} 286 @Override onCdmaConferenceSwap(Call call)287 public void onCdmaConferenceSwap(Call call) {} 288 @Override onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout)289 public boolean onCanceledViaNewOutgoingCallBroadcast(Call call, long disconnectionTimeout) { 290 return false; 291 } 292 @Override onHoldToneRequested(Call call)293 public void onHoldToneRequested(Call call) {} 294 @Override onCallHoldFailed(Call call)295 public void onCallHoldFailed(Call call) {} 296 @Override onCallSwitchFailed(Call call)297 public void onCallSwitchFailed(Call call) {} 298 @Override onCallResumeFailed(Call call)299 public void onCallResumeFailed(Call call) {} 300 @Override onConnectionEvent(Call call, String event, Bundle extras)301 public void onConnectionEvent(Call call, String event, Bundle extras) {} 302 @Override onCallStreamingStateChanged(Call call, boolean isStreaming)303 public void onCallStreamingStateChanged(Call call, boolean isStreaming) {} 304 @Override onExternalCallChanged(Call call, boolean isExternalCall)305 public void onExternalCallChanged(Call call, boolean isExternalCall) {} 306 @Override onRttInitiationFailure(Call call, int reason)307 public void onRttInitiationFailure(Call call, int reason) {} 308 @Override onRemoteRttRequest(Call call, int requestId)309 public void onRemoteRttRequest(Call call, int requestId) {} 310 @Override onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, Bundle extras, boolean isLegacy)311 public void onHandoverRequested(Call call, PhoneAccountHandle handoverTo, int videoState, 312 Bundle extras, boolean isLegacy) {} 313 @Override onHandoverFailed(Call call, int error)314 public void onHandoverFailed(Call call, int error) {} 315 @Override onHandoverComplete(Call call)316 public void onHandoverComplete(Call call) {} 317 @Override onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report)318 public void onBluetoothCallQualityReport(Call call, BluetoothCallQualityReport report) {} 319 @Override onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue)320 public void onReceivedDeviceToDeviceMessage(Call call, int messageType, int messageValue) {} 321 @Override onReceivedCallQualityReport(Call call, CallQuality callQuality)322 public void onReceivedCallQualityReport(Call call, CallQuality callQuality) {} 323 @Override onCallerNumberVerificationStatusChanged(Call call, int callerNumberVerificationStatus)324 public void onCallerNumberVerificationStatusChanged(Call call, 325 int callerNumberVerificationStatus) {} 326 } 327 328 private final CallerInfoLookupHelper.OnQueryCompleteListener mCallerInfoQueryListener = 329 new CallerInfoLookupHelper.OnQueryCompleteListener() { 330 /** ${inheritDoc} */ 331 @Override 332 public void onCallerInfoQueryComplete(Uri handle, CallerInfo callerInfo) { 333 synchronized (mLock) { 334 Call call = Call.this; 335 if (call != null) { 336 call.setCallerInfo(handle, callerInfo); 337 } 338 } 339 } 340 341 @Override 342 public void onContactPhotoQueryComplete(Uri handle, CallerInfo callerInfo) { 343 synchronized (mLock) { 344 Call call = Call.this; 345 if (call != null) { 346 call.setCallerInfo(handle, callerInfo); 347 } 348 } 349 } 350 }; 351 352 private final boolean mIsModifyStatePermissionGranted; 353 /** 354 * One of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING, or CALL_DIRECTION_UNKNOWN 355 */ 356 private int mCallDirection; 357 358 /** 359 * The post-dial digits that were dialed after the network portion of the number 360 */ 361 private String mPostDialDigits; 362 363 /** 364 * The secondary line number that an incoming call has been received on if the SIM subscription 365 * has multiple associated numbers. 366 */ 367 private String mViaNumber = ""; 368 369 /** 370 * The wall clock time this call was created. Beyond logging and such, may also be used for 371 * bookkeeping and specifically for marking certain call attempts as failed attempts. 372 * Note: This timestamp should NOT be used for calculating call duration. 373 */ 374 private long mCreationTimeMillis; 375 376 /** 377 * The elapsed realtime millis when this call was created; this can be used to determine how 378 * long has elapsed since the call was first created. 379 */ 380 private long mCreationElapsedRealtimeMillis; 381 382 /** The time this call was made active. */ 383 private long mConnectTimeMillis = 0; 384 385 /** 386 * The time, in millis, since boot when this call was connected. This should ONLY be used when 387 * calculating the duration of the call. 388 * 389 * The reason for this is that the {@link SystemClock#elapsedRealtime()} is based on the 390 * elapsed time since the device was booted. Changes to the system clock (e.g. due to NITZ 391 * time sync, time zone changes user initiated clock changes) would cause a duration calculated 392 * based on {@link #mConnectTimeMillis} to change based on the delta in the time. 393 * Using the {@link SystemClock#elapsedRealtime()} ensures that changes to the wall clock do 394 * not impact the call duration. 395 */ 396 private long mConnectElapsedTimeMillis = 0; 397 398 /** The wall clock time this call was disconnected. */ 399 private long mDisconnectTimeMillis = 0; 400 401 /** 402 * The elapsed time since boot when this call was disconnected. Recorded as the 403 * {@link SystemClock#elapsedRealtime()}. This ensures that the call duration is not impacted 404 * by changes in the wall time clock. 405 */ 406 private long mDisconnectElapsedTimeMillis = 0; 407 408 /** The gateway information associated with this call. This stores the original call handle 409 * that the user is attempting to connect to via the gateway, the actual handle to dial in 410 * order to connect the call via the gateway, as well as the package name of the gateway 411 * service. */ 412 private GatewayInfo mGatewayInfo; 413 414 private PhoneAccountHandle mConnectionManagerPhoneAccountHandle; 415 416 private PhoneAccountHandle mTargetPhoneAccountHandle; 417 418 private PhoneAccountHandle mRemotePhoneAccountHandle; 419 420 private UserHandle mAssociatedUser; 421 422 private final Handler mHandler = new Handler(Looper.getMainLooper()); 423 424 private final List<Call> mConferenceableCalls = new ArrayList<>(); 425 426 /** The state of the call. */ 427 private int mState; 428 429 /** 430 * Determines whether the {@link ConnectionService} has responded to the initial request to 431 * create the connection. 432 * 433 * {@code false} indicates the {@link Call} has been added to Telecom, but the 434 * {@link Connection} has not yet been returned by the associated {@link ConnectionService}. 435 * {@code true} indicates the {@link Call} has an associated {@link Connection} reported by the 436 * {@link ConnectionService}. 437 */ 438 private boolean mIsCreateConnectionComplete = false; 439 440 /** The handle with which to establish this call. */ 441 private Uri mHandle; 442 443 /** The participants with which to establish adhoc conference call */ 444 private List<Uri> mParticipants; 445 /** 446 * The presentation requirements for the handle. See {@link TelecomManager} for valid values. 447 */ 448 private int mHandlePresentation; 449 450 /** 451 * The verification status for an incoming call's number. 452 */ 453 private @Connection.VerificationStatus int mCallerNumberVerificationStatus; 454 455 /** The caller display name (CNAP) set by the connection service. */ 456 private String mCallerDisplayName; 457 458 /** 459 * The presentation requirements for the handle. See {@link TelecomManager} for valid values. 460 */ 461 private int mCallerDisplayNamePresentation; 462 463 /** 464 * The remote connection service which is attempted or already connecting this call. This is set 465 * to a non-null value only when a connection manager phone account is in use. When set, this 466 * will correspond to the target phone account of the {@link Call}. 467 */ 468 private ConnectionServiceWrapper mRemoteConnectionService; 469 470 /** 471 * The connection service which is attempted or already connecting this call. 472 */ 473 private ConnectionServiceWrapper mConnectionService; 474 475 private TransactionalServiceWrapper mTransactionalService; 476 477 private boolean mIsEmergencyCall; 478 479 /** 480 * Flag indicating if ECBM is active for the target phone account. This only applies to MT calls 481 * in the scenario of work profiles (when the profile is paused and the user has only registered 482 * a work sim). Normally, MT calls made to the work sim should be rejected when the work apps 483 * are paused. However, when the admin makes a MO ecall, ECBM should be enabled for that sim to 484 * allow non-emergency MT calls. MO calls don't apply because the phone account would be 485 * rejected from selection if the owner is not placing the call. 486 */ 487 private boolean mIsInECBM; 488 489 // The Call is considered an emergency call for testing, but will not actually connect to 490 // emergency services. 491 private boolean mIsTestEmergencyCall; 492 493 private boolean mSpeakerphoneOn; 494 495 private boolean mIsDisconnectingChildCall = false; 496 497 /** 498 * Tracks the video states which were applicable over the duration of a call. 499 * See {@link VideoProfile} for a list of valid video states. 500 * <p> 501 * Video state history is tracked when the call is active, and when a call is rejected or 502 * missed. 503 */ 504 private int mVideoStateHistory; 505 506 private int mVideoState; 507 508 /** 509 * Disconnect cause for the call. Only valid if the state of the call is STATE_DISCONNECTED. 510 * See {@link android.telecom.DisconnectCause}. 511 */ 512 private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN); 513 514 /** 515 * Override the disconnect cause set by the connection service. Used for audio processing and 516 * simulated ringing calls as well as the condition when an emergency call is ended due to 517 * an emergency call being placed. 518 */ 519 private DisconnectCause mOverrideDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN); 520 521 /** 522 * Simultaneous type of the call. 523 */ 524 private int mSimultaneousType = CALL_SIMULTANEOUS_UNKNOWN; 525 526 /** 527 * Indicate whether the call has the video 528 */ 529 boolean mHasVideoCall; 530 531 private Bundle mIntentExtras = new Bundle(); 532 533 /** 534 * The {@link Intent} which originally created this call. Only populated when we are putting a 535 * call into a pending state and need to pick up initiation of the call later. 536 */ 537 private Intent mOriginalCallIntent = null; 538 539 /** Set of listeners on this call. 540 * 541 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is 542 * load factor before resizing, 1 means we only expect a single thread to 543 * access the map so make only a single shard 544 */ 545 private final Set<Listener> mListeners = Collections.newSetFromMap( 546 new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1)); 547 548 private CreateConnectionProcessor mCreateConnectionProcessor; 549 550 /** Caller information retrieved from the latest contact query. */ 551 private CallerInfo mCallerInfo; 552 553 /** The latest token used with a contact info query. */ 554 private int mQueryToken = 0; 555 556 /** Whether this call is requesting that Telecom play the ringback tone on its behalf. */ 557 private boolean mRingbackRequested = false; 558 559 /** Whether this call is requesting to be silently ringing. */ 560 private boolean mSilentRingingRequested = false; 561 562 /** Whether direct-to-voicemail query is pending. */ 563 private boolean mDirectToVoicemailQueryPending; 564 565 private int mConnectionCapabilities; 566 567 private int mConnectionProperties; 568 569 private int mSupportedAudioRoutes = CallAudioState.ROUTE_ALL; 570 571 private boolean mIsConference = false; 572 573 private boolean mHadChildren = false; 574 575 private final boolean mShouldAttachToExistingConnection; 576 577 private Call mParentCall = null; 578 579 private List<Call> mChildCalls = new LinkedList<>(); 580 581 /** Set of text message responses allowed for this call, if applicable. */ 582 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST; 583 584 /** Whether an attempt has been made to load the text message responses. */ 585 private boolean mCannedSmsResponsesLoadingStarted = false; 586 587 private VideoProviderProxy mVideoProviderProxy; 588 589 private boolean mIsVoipAudioMode; 590 private StatusHints mStatusHints; 591 private Bundle mExtras; 592 private final ConnectionServiceRepository mRepository; 593 private final Context mContext; 594 private final CallsManager mCallsManager; 595 private final ClockProxy mClockProxy; 596 private final ToastFactory mToastFactory; 597 private final TelecomSystem.SyncRoot mLock; 598 private final String mId; 599 private String mConnectionId; 600 private Analytics.CallInfo mAnalytics = new Analytics.CallInfo(); 601 private CallStateChangedAtomWriter mCallStateChangedAtomWriter = 602 new CallStateChangedAtomWriter(); 603 private char mPlayingDtmfTone; 604 605 private boolean mWasConferencePreviouslyMerged = false; 606 private boolean mWasHighDefAudio = false; 607 private boolean mWasWifi = false; 608 private boolean mWasVolte = false; 609 private boolean mDestroyed = false; 610 611 // For conferences which support merge/swap at their level, we retain a notion of an active 612 // call. This is used for BluetoothPhoneService. In order to support hold/merge, it must have 613 // the notion of the current "active" call within the conference call. This maintains the 614 // "active" call and switches every time the user hits "swap". 615 private Call mConferenceLevelActiveCall = null; 616 617 private boolean mIsLocallyDisconnecting = false; 618 619 /** 620 * Tracks the current call data usage as reported by the video provider. 621 */ 622 private long mCallDataUsage = DATA_USAGE_NOT_SET; 623 624 private boolean mIsWorkCall; 625 626 /** 627 * Tracks whether this {@link Call}'s {@link #getTargetPhoneAccount()} has 628 * {@link PhoneAccount#EXTRA_PLAY_CALL_RECORDING_TONE} set. 629 */ 630 private boolean mUseCallRecordingTone; 631 632 // Set to true once the NewOutgoingCallIntentBroadcast comes back and is processed. 633 private boolean mIsNewOutgoingCallIntentBroadcastDone = false; 634 635 /** 636 * Indicates whether the call is remotely held. A call is considered remotely held when 637 * {@link #onConnectionEvent(String)} receives the {@link Connection#EVENT_ON_HOLD_TONE_START} 638 * event. 639 */ 640 private boolean mIsRemotelyHeld = false; 641 642 /** 643 * Indicates whether the {@link PhoneAccount} associated with this call is self-managed. 644 * See {@link PhoneAccount#CAPABILITY_SELF_MANAGED} for more information. 645 */ 646 private boolean mIsSelfManaged = false; 647 648 private boolean mIsTransactionalCall = false; 649 private CallingPackageIdentity mCallingPackageIdentity = new CallingPackageIdentity(); 650 private boolean mSkipAutoUnhold = false; 651 652 /** 653 * CallingPackageIdentity is responsible for storing properties about the calling package that 654 * initiated the call. For example, if MyVoipApp requests to add a call with Telecom, we can 655 * store their UID and PID when we are still bound to that package. 656 */ 657 public static class CallingPackageIdentity { 658 public int mCallingPackageUid = -1; 659 public int mCallingPackagePid = -1; 660 CallingPackageIdentity()661 public CallingPackageIdentity() { 662 } 663 CallingPackageIdentity(Bundle extras)664 CallingPackageIdentity(Bundle extras) { 665 mCallingPackageUid = extras.getInt(CallAttributes.CALLER_UID_KEY, -1); 666 mCallingPackagePid = extras.getInt(CallAttributes.CALLER_PID_KEY, -1); 667 } 668 } 669 670 /** 671 * Indicates whether this call is streaming. 672 */ 673 private boolean mIsStreaming = false; 674 675 /** 676 * Indicates whether the {@link PhoneAccount} associated with an self-managed call want to 677 * expose the call to an {@link android.telecom.InCallService} which declares the metadata 678 * {@link TelecomManager#METADATA_INCLUDE_SELF_MANAGED_CALLS}, 679 * For calls that {@link #mIsSelfManaged} is {@code false}, this value should be {@code false} 680 * as well. 681 */ 682 private boolean mVisibleToInCallService = false; 683 684 /** 685 * Indicates whether the {@link PhoneAccount} associated with this call supports video calling. 686 * {@code True} if the phone account supports video calling, {@code false} otherwise. 687 */ 688 private boolean mIsVideoCallingSupportedByPhoneAccount = false; 689 690 /** 691 * Indicates whether this individual calls video state can be changed as opposed to be gated 692 * by the {@link PhoneAccount}. 693 * 694 * {@code True} if the call is Transactional && has the CallAttributes.SUPPORTS_VIDEO_CALLING 695 * capability {@code false} otherwise. 696 */ 697 private boolean mTransactionalCallSupportsVideoCalling = false; 698 setTransactionalCallSupportsVideoCalling(CallAttributes callAttributes)699 public void setTransactionalCallSupportsVideoCalling(CallAttributes callAttributes) { 700 if (!mIsTransactionalCall) { 701 Log.i(this, "setTransactionalCallSupportsVideoCalling: call is not transactional"); 702 return; 703 } 704 if (callAttributes == null) { 705 Log.i(this, "setTransactionalCallSupportsVideoCalling: callAttributes is null"); 706 return; 707 } 708 if ((callAttributes.getCallCapabilities() & CallAttributes.SUPPORTS_VIDEO_CALLING) 709 == CallAttributes.SUPPORTS_VIDEO_CALLING) { 710 mTransactionalCallSupportsVideoCalling = true; 711 } else { 712 mTransactionalCallSupportsVideoCalling = false; 713 } 714 } 715 isTransactionalCallSupportsVideoCalling()716 public boolean isTransactionalCallSupportsVideoCalling() { 717 return mTransactionalCallSupportsVideoCalling; 718 } 719 720 /** 721 * Indicates whether or not this call can be pulled if it is an external call. If true, respect 722 * the Connection Capability set by the ConnectionService. If false, override the capability 723 * set and always remove the ability to pull this external call. 724 * 725 * See {@link #setIsPullExternalCallSupported(boolean)} 726 */ 727 private boolean mIsPullExternalCallSupported = true; 728 729 private PhoneNumberUtilsAdapter mPhoneNumberUtilsAdapter; 730 731 /** 732 * For {@link Connection}s or {@link android.telecom.Conference}s added via a ConnectionManager 733 * using the {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle, 734 * Connection)} or {@link android.telecom.ConnectionService#addConference(Conference)}, 735 * indicates the ID of this call as it was referred to by the {@code ConnectionService} which 736 * originally created it. 737 * 738 * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID} for more information. 739 */ 740 private String mOriginalConnectionId; 741 742 /** 743 * Two pairs of {@link android.os.ParcelFileDescriptor}s that handle RTT text communication 744 * between the in-call app and the connection service. If both non-null, this call should be 745 * treated as an RTT call. 746 * Each array should be of size 2. First one is the read side and the second one is the write 747 * side. 748 */ 749 private ParcelFileDescriptor[] mInCallToConnectionServiceStreams; 750 private ParcelFileDescriptor[] mConnectionServiceToInCallStreams; 751 752 /** 753 * True if we're supposed to start this call with RTT, either due to the settings switch or due 754 * to an extra. 755 */ 756 private boolean mDidRequestToStartWithRtt = false; 757 /** 758 * Integer constant from {@link android.telecom.Call.RttCall}. Describes the current RTT mode. 759 */ 760 private int mRttMode; 761 /** 762 * True if the call was ever an RTT call. 763 */ 764 private boolean mWasEverRtt = false; 765 766 /** 767 * Integer indicating the remote RTT request ID that is pending a response from the user. 768 */ 769 private int mPendingRttRequestId = INVALID_RTT_REQUEST_ID; 770 771 /** 772 * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle, 773 * int, Bundle, boolean)}, contains the call which this call is being handed over to. 774 */ 775 private Call mHandoverDestinationCall = null; 776 777 /** 778 * When a call handover has been initiated via {@link #requestHandover(PhoneAccountHandle, 779 * int, Bundle, boolean)}, contains the call which this call is being handed over from. 780 */ 781 private Call mHandoverSourceCall = null; 782 783 /** 784 * The user-visible app name of the app that requested for this call to be put into the 785 * AUDIO_PROCESSING state. Used to display a notification to the user. 786 */ 787 private CharSequence mAudioProcessingRequestingApp = null; 788 789 /** 790 * Indicates the current state of this call if it is in the process of a handover. 791 */ 792 private int mHandoverState = HandoverState.HANDOVER_NONE; 793 794 /** 795 * Indicates whether this call is using one of the 796 * {@link com.android.server.telecom.callfiltering.CallFilter} modules. 797 */ 798 private boolean mIsUsingCallFiltering = false; 799 800 /** 801 * Indicates whether or not this call has been active before. This is helpful in detecting 802 * situations where we have moved into {@link CallState#SIMULATED_RINGING} or 803 * {@link CallState#AUDIO_PROCESSING} again after being active. If a call has moved into one 804 * of these states again after being active and the user dials an emergency call, we want to 805 * log these calls normally instead of considering them MISSED. If the emergency call was 806 * dialed during initial screening however, we want to treat those calls as MISSED (because the 807 * user never got the chance to explicitly reject). 808 */ 809 private boolean mHasGoneActiveBefore = false; 810 811 /** 812 * Indicates the package name of the {@link android.telecom.CallScreeningService} which should 813 * be sent the {@link android.telecom.TelecomManager#ACTION_POST_CALL} intent upon disconnection 814 * of a call. 815 */ 816 private String mPostCallPackageName; 817 818 /** 819 * Call missed information code. 820 */ 821 @CallLog.Calls.MissedReason private long mMissedReason; 822 823 /** 824 * Time that this call start ringing or simulated ringing. 825 */ 826 private long mStartRingTime; 827 828 /** 829 * The package name of the call screening service that silence this call. If the call is not 830 * silenced, this field will be null. 831 */ 832 private CharSequence mCallScreeningAppName; 833 834 /** 835 * The component name of the call screening service that silence this call. If the call is not 836 * silenced, this field will be null. 837 */ 838 private String mCallScreeningComponentName; 839 840 /** 841 * When {@code true} indicates this call originated from a SIM-based {@link PhoneAccount}. 842 * A sim-based {@link PhoneAccount} is one with {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION} 843 * set. 844 */ 845 private boolean mIsSimCall; 846 847 /** 848 * Set to {@code true} if we received a valid response ({@code null} or otherwise) from 849 * the {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)} or 850 * {@link CallDiagnostics#onCallDisconnected(int, int)} calls. This is used to detect a timeout 851 * when awaiting a response from the call diagnostic service. 852 */ 853 private boolean mReceivedCallDiagnosticPostCallResponse = false; 854 855 /** 856 * {@link CompletableFuture} used to delay posting disconnection and removal to a call until 857 * after a {@link CallDiagnosticService} is able to handle the disconnection and provide a 858 * disconnect message via {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)} or 859 * {@link CallDiagnostics#onCallDisconnected(int, int)}. 860 */ 861 private CompletableFuture<Boolean> mDiagnosticCompleteFuture; 862 863 /** 864 * {@link CompletableFuture} used to perform disconnect operations after 865 * {@link #mDiagnosticCompleteFuture} has completed. 866 */ 867 private CompletableFuture<Void> mDisconnectFuture; 868 869 /** 870 * {@link CompletableFuture} used to perform call removal operations after the 871 * {@link #mDisconnectFuture} has completed. 872 * <p> 873 * Note: It is possible for this future to be cancelled in the case that an internal operation 874 * will be handling clean up. (See {@link #setState}.) 875 */ 876 private CompletableFuture<Void> mRemovalFuture; 877 878 /** 879 * {@link CompletableFuture} used to delay audio routing change for a ringing call until the 880 * corresponding bluetooth {@link android.telecom.InCallService} is successfully bound or timed 881 * out. 882 */ 883 private CompletableFuture<Boolean> mBtIcsFuture; 884 885 /** 886 * Map of CachedCallbacks that are pending to be executed when the *ServiceWrapper connects 887 */ 888 private final Map<String, List<CachedCallback>> mCachedServiceCallbacks = new HashMap<>(); 889 cacheServiceCallback(CachedCallback callback)890 public void cacheServiceCallback(CachedCallback callback) { 891 synchronized (mCachedServiceCallbacks) { 892 if (mFlags.cacheCallEvents()) { 893 // If there are multiple threads caching + calling processCachedCallbacks at the 894 // same time, there is a race - double check here to ensure that we do not lose an 895 // operation due to a a cache happening after processCachedCallbacks. 896 // Either service will be non-null in this case, but both will not be non-null 897 if (mConnectionService != null) { 898 callback.executeCallback(mConnectionService, this); 899 return; 900 } 901 if (mTransactionalService != null) { 902 callback.executeCallback(mTransactionalService, this); 903 return; 904 } 905 } 906 List<CachedCallback> cbs = mCachedServiceCallbacks.computeIfAbsent( 907 callback.getCallbackId(), k -> new ArrayList<>()); 908 switch (callback.getCacheType()) { 909 case TYPE_STATE: { 910 cbs.clear(); 911 cbs.add(callback); 912 break; 913 } 914 case TYPE_QUEUE: { 915 cbs.add(callback); 916 } 917 } 918 } 919 } 920 921 @VisibleForTesting getCachedServiceCallbacksCopy()922 public Map<String, List<CachedCallback>> getCachedServiceCallbacksCopy() { 923 synchronized (mCachedServiceCallbacks) { 924 // This should only be used during testing, but to be safe, since there is internally a 925 // List value, we need to do a deep copy to ensure someone with a ref to the Map doesn't 926 // mutate the underlying list while we are modifying it in cacheServiceCallback. 927 return mCachedServiceCallbacks.entrySet().stream().collect( 928 Collectors.toUnmodifiableMap(Map.Entry::getKey, e-> List.copyOf(e.getValue()))); 929 } 930 } 931 932 private FeatureFlags mFlags; 933 934 /** 935 * Persists the specified parameters and initializes the new instance. 936 * @param context The context. 937 * @param repository The connection service repository. 938 * @param handle The handle to dial. 939 * @param gatewayInfo Gateway information to use for the call. 940 * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call. 941 * This account must be one that was registered with the 942 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag. 943 * @param targetPhoneAccountHandle Account information to use for the call. This account must be 944 * one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag. 945 * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING, 946 * or CALL_DIRECTION_UNKNOWN. 947 * @param shouldAttachToExistingConnection Set to true to attach the call to an existing 948 * @param clockProxy 949 */ Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, ClockProxy clockProxy, ToastFactory toastFactory, FeatureFlags featureFlags)950 public Call( 951 String callId, 952 Context context, 953 CallsManager callsManager, 954 TelecomSystem.SyncRoot lock, 955 ConnectionServiceRepository repository, 956 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, 957 Uri handle, 958 GatewayInfo gatewayInfo, 959 PhoneAccountHandle connectionManagerPhoneAccountHandle, 960 PhoneAccountHandle targetPhoneAccountHandle, 961 int callDirection, 962 boolean shouldAttachToExistingConnection, 963 boolean isConference, 964 ClockProxy clockProxy, 965 ToastFactory toastFactory, 966 FeatureFlags featureFlags) { 967 this(callId, context, callsManager, lock, repository, phoneNumberUtilsAdapter, 968 handle, null, gatewayInfo, connectionManagerPhoneAccountHandle, 969 targetPhoneAccountHandle, callDirection, shouldAttachToExistingConnection, 970 isConference, clockProxy, toastFactory, featureFlags); 971 972 } 973 Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, List<Uri> participants, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, ClockProxy clockProxy, ToastFactory toastFactory, FeatureFlags featureFlags)974 public Call( 975 String callId, 976 Context context, 977 CallsManager callsManager, 978 TelecomSystem.SyncRoot lock, 979 ConnectionServiceRepository repository, 980 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, 981 Uri handle, 982 List<Uri> participants, 983 GatewayInfo gatewayInfo, 984 PhoneAccountHandle connectionManagerPhoneAccountHandle, 985 PhoneAccountHandle targetPhoneAccountHandle, 986 int callDirection, 987 boolean shouldAttachToExistingConnection, 988 boolean isConference, 989 ClockProxy clockProxy, 990 ToastFactory toastFactory, 991 FeatureFlags featureFlags) { 992 mFlags = featureFlags; 993 mId = callId; 994 mConnectionId = callId; 995 mState = (isConference && callDirection != CALL_DIRECTION_INCOMING && 996 callDirection != CALL_DIRECTION_OUTGOING) ? 997 CallState.ACTIVE : CallState.NEW; 998 mContext = context; 999 mCallsManager = callsManager; 1000 mLock = lock; 1001 mRepository = repository; 1002 mPhoneNumberUtilsAdapter = phoneNumberUtilsAdapter; 1003 mParticipants = participants; 1004 mPostDialDigits = handle != null 1005 ? PhoneNumberUtils.extractPostDialPortion(handle.getSchemeSpecificPart()) : ""; 1006 mGatewayInfo = gatewayInfo; 1007 setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle); 1008 mCallDirection = callDirection; 1009 setTargetPhoneAccount(targetPhoneAccountHandle); 1010 setHandle(handle); 1011 mIsConference = isConference; 1012 mShouldAttachToExistingConnection = shouldAttachToExistingConnection 1013 || callDirection == CALL_DIRECTION_INCOMING; 1014 maybeLoadCannedSmsResponses(); 1015 mClockProxy = clockProxy; 1016 mToastFactory = toastFactory; 1017 mCreationTimeMillis = mClockProxy.currentTimeMillis(); 1018 mCreationElapsedRealtimeMillis = mClockProxy.elapsedRealtime(); 1019 mMissedReason = MISSED_REASON_NOT_MISSED; 1020 mStartRingTime = 0; 1021 1022 mCallStateChangedAtomWriter.setExistingCallCount(callsManager.getCalls().size()); 1023 mIsModifyStatePermissionGranted = 1024 isModifyPhoneStatePermissionGranted(getDelegatePhoneAccountHandle()); 1025 } 1026 1027 /** 1028 * Persists the specified parameters and initializes the new instance. 1029 * @param context The context. 1030 * @param repository The connection service repository. 1031 * @param handle The handle to dial. 1032 * @param gatewayInfo Gateway information to use for the call. 1033 * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call. 1034 * This account must be one that was registered with the 1035 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag. 1036 * @param targetPhoneAccountHandle Account information to use for the call. This account must be 1037 * one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag. 1038 * @param callDirection one of CALL_DIRECTION_INCOMING, CALL_DIRECTION_OUTGOING, 1039 * or CALL_DIRECTION_UNKNOWN 1040 * @param shouldAttachToExistingConnection Set to true to attach the call to an existing 1041 * connection, regardless of whether it's incoming or outgoing. 1042 * @param connectTimeMillis The connection time of the call. 1043 * @param clockProxy 1044 * @param featureFlags The telecom feature flags. 1045 */ Call( String callId, Context context, CallsManager callsManager, TelecomSystem.SyncRoot lock, ConnectionServiceRepository repository, PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, Uri handle, GatewayInfo gatewayInfo, PhoneAccountHandle connectionManagerPhoneAccountHandle, PhoneAccountHandle targetPhoneAccountHandle, int callDirection, boolean shouldAttachToExistingConnection, boolean isConference, long connectTimeMillis, long connectElapsedTimeMillis, ClockProxy clockProxy, ToastFactory toastFactory, FeatureFlags featureFlags)1046 Call( 1047 String callId, 1048 Context context, 1049 CallsManager callsManager, 1050 TelecomSystem.SyncRoot lock, 1051 ConnectionServiceRepository repository, 1052 PhoneNumberUtilsAdapter phoneNumberUtilsAdapter, 1053 Uri handle, 1054 GatewayInfo gatewayInfo, 1055 PhoneAccountHandle connectionManagerPhoneAccountHandle, 1056 PhoneAccountHandle targetPhoneAccountHandle, 1057 int callDirection, 1058 boolean shouldAttachToExistingConnection, 1059 boolean isConference, 1060 long connectTimeMillis, 1061 long connectElapsedTimeMillis, 1062 ClockProxy clockProxy, 1063 ToastFactory toastFactory, 1064 FeatureFlags featureFlags) { 1065 this(callId, context, callsManager, lock, repository, 1066 phoneNumberUtilsAdapter, handle, gatewayInfo, 1067 connectionManagerPhoneAccountHandle, targetPhoneAccountHandle, callDirection, 1068 shouldAttachToExistingConnection, isConference, clockProxy, toastFactory, 1069 featureFlags); 1070 1071 mConnectTimeMillis = connectTimeMillis; 1072 mConnectElapsedTimeMillis = connectElapsedTimeMillis; 1073 mAnalytics.setCallStartTime(connectTimeMillis); 1074 } 1075 addListener(Listener listener)1076 public void addListener(Listener listener) { 1077 mListeners.add(listener); 1078 } 1079 removeListener(Listener listener)1080 public void removeListener(Listener listener) { 1081 if (listener != null) { 1082 mListeners.remove(listener); 1083 } 1084 } 1085 initAnalytics()1086 public void initAnalytics() { 1087 initAnalytics(null, null); 1088 } 1089 initAnalytics(String callingPackage, String extraCreationLogs)1090 public void initAnalytics(String callingPackage, String extraCreationLogs) { 1091 int analyticsDirection; 1092 switch (mCallDirection) { 1093 case CALL_DIRECTION_OUTGOING: 1094 analyticsDirection = Analytics.OUTGOING_DIRECTION; 1095 break; 1096 case CALL_DIRECTION_INCOMING: 1097 analyticsDirection = Analytics.INCOMING_DIRECTION; 1098 break; 1099 case CALL_DIRECTION_UNKNOWN: 1100 case CALL_DIRECTION_UNDEFINED: 1101 default: 1102 analyticsDirection = Analytics.UNKNOWN_DIRECTION; 1103 } 1104 mAnalytics = Analytics.initiateCallAnalytics(mId, analyticsDirection); 1105 mAnalytics.setCallIsEmergency(mIsEmergencyCall); 1106 Log.addEvent(this, LogUtils.Events.CREATED, callingPackage + ";" + extraCreationLogs); 1107 } 1108 getAnalytics()1109 public Analytics.CallInfo getAnalytics() { 1110 return mAnalytics; 1111 } 1112 destroy()1113 public void destroy() { 1114 if (mDestroyed) { 1115 return; 1116 } 1117 // We should not keep these bitmaps around because the Call objects may be held for logging 1118 // purposes. 1119 // TODO: Make a container object that only stores the information we care about for Logging. 1120 if (mCallerInfo != null) { 1121 mCallerInfo.cachedPhotoIcon = null; 1122 mCallerInfo.cachedPhoto = null; 1123 } 1124 closeRttStreams(); 1125 1126 Log.addEvent(this, LogUtils.Events.DESTROYED); 1127 mDestroyed = true; 1128 } 1129 closeRttStreams()1130 private void closeRttStreams() { 1131 if (mConnectionServiceToInCallStreams != null) { 1132 for (ParcelFileDescriptor fd : mConnectionServiceToInCallStreams) { 1133 if (fd != null) { 1134 try { 1135 fd.close(); 1136 } catch (IOException e) { 1137 // ignore 1138 } 1139 } 1140 } 1141 } 1142 if (mInCallToConnectionServiceStreams != null) { 1143 for (ParcelFileDescriptor fd : mInCallToConnectionServiceStreams) { 1144 if (fd != null) { 1145 try { 1146 fd.close(); 1147 } catch (IOException e) { 1148 // ignore 1149 } 1150 } 1151 } 1152 } 1153 } 1154 1155 /** {@inheritDoc} */ 1156 @Override toString()1157 public String toString() { 1158 return String.format(Locale.US, "[Call id=%s, state=%s, tpac=%s, cmgr=%s, handle=%s, " 1159 + "vidst=%s, childs(%d), has_parent(%b), cap=%s, prop=%s], voip=%b", 1160 mId, 1161 CallState.toString(getParcelableCallState()), 1162 getTargetPhoneAccount(), 1163 getConnectionManagerPhoneAccount(), 1164 Log.piiHandle(mHandle), 1165 getVideoStateDescription(getVideoState()), 1166 getChildCalls().size(), 1167 getParentCall() != null, 1168 Connection.capabilitiesToStringShort(getConnectionCapabilities()), 1169 Connection.propertiesToStringShort(getConnectionProperties()), 1170 mIsVoipAudioMode); 1171 } 1172 1173 @Override getDescription()1174 public String getDescription() { 1175 StringBuilder s = new StringBuilder(); 1176 if (isSelfManaged()) { 1177 s.append("SelfMgd Call"); 1178 } else if (isExternalCall()) { 1179 s.append("External Call"); 1180 } else { 1181 s.append("Call"); 1182 } 1183 s.append(getId()); 1184 s.append(" ["); 1185 s.append(SimpleDateFormat.getDateTimeInstance().format(new Date(getCreationTimeMillis()))); 1186 s.append("]"); 1187 s.append(isIncoming() ? "(MT - incoming)" : "(MO - outgoing)"); 1188 s.append("(User="); 1189 s.append(getAssociatedUser()); 1190 s.append(")"); 1191 s.append("\n\t"); 1192 1193 PhoneAccountHandle targetPhoneAccountHandle = getTargetPhoneAccount(); 1194 PhoneAccountHandle remotePhoneAccountHandle = getRemotePhoneAccountHandle(); 1195 PhoneAccountHandle connectionMgrAccountHandle = getConnectionManagerPhoneAccount(); 1196 PhoneAccountHandle delegatePhoneAccountHandle = getDelegatePhoneAccountHandle(); 1197 boolean isTargetSameAsRemote = targetPhoneAccountHandle != null 1198 && targetPhoneAccountHandle.equals(remotePhoneAccountHandle); 1199 if (Objects.equals(delegatePhoneAccountHandle, targetPhoneAccountHandle)) { 1200 s.append(">>>"); 1201 } 1202 s.append("Target"); 1203 s.append(" PhoneAccount: "); 1204 if (targetPhoneAccountHandle != null) { 1205 s.append(targetPhoneAccountHandle); 1206 s.append(" ("); 1207 s.append(getTargetPhoneAccountLabel()); 1208 s.append(")"); 1209 if (isTargetSameAsRemote) { 1210 s.append("(remote)"); 1211 } 1212 } else { 1213 s.append("not set"); 1214 } 1215 if (!isTargetSameAsRemote && remotePhoneAccountHandle != null) { 1216 // This is a RARE case and will likely not be seen in practice but it is possible. 1217 if (delegatePhoneAccountHandle.equals(remotePhoneAccountHandle)) { 1218 s.append("\n\t>>>Remote PhoneAccount: "); 1219 } else { 1220 s.append("\n\tRemote PhoneAccount: "); 1221 } 1222 s.append(remotePhoneAccountHandle); 1223 } 1224 if (connectionMgrAccountHandle != null) { 1225 if (delegatePhoneAccountHandle.equals(connectionMgrAccountHandle)) { 1226 s.append("\n\t>>>Conn mgr: "); 1227 } else { 1228 s.append("\n\tConn mgr: "); 1229 } 1230 s.append(connectionMgrAccountHandle); 1231 } 1232 1233 s.append("\n\tTo address: "); 1234 s.append(Log.piiHandle(getHandle())); 1235 if (isIncoming()) { 1236 switch (mCallerNumberVerificationStatus) { 1237 case Connection.VERIFICATION_STATUS_FAILED: 1238 s.append(" Verstat: fail"); 1239 break; 1240 case Connection.VERIFICATION_STATUS_NOT_VERIFIED: 1241 s.append(" Verstat: not"); 1242 break; 1243 case Connection.VERIFICATION_STATUS_PASSED: 1244 s.append(" Verstat: pass"); 1245 break; 1246 } 1247 } 1248 s.append(" Presentation: "); 1249 switch (getHandlePresentation()) { 1250 case TelecomManager.PRESENTATION_ALLOWED: 1251 s.append("Allowed"); 1252 break; 1253 case TelecomManager.PRESENTATION_PAYPHONE: 1254 s.append("Payphone"); 1255 break; 1256 case TelecomManager.PRESENTATION_RESTRICTED: 1257 s.append("Restricted"); 1258 break; 1259 case TelecomManager.PRESENTATION_UNKNOWN: 1260 s.append("Unknown"); 1261 break; 1262 case TelecomManager.PRESENTATION_UNAVAILABLE: 1263 s.append("Unavailable"); 1264 break; 1265 default: 1266 s.append("<undefined>"); 1267 } 1268 s.append("\n"); 1269 return s.toString(); 1270 } 1271 1272 /** 1273 * Builds a debug-friendly description string for a video state. 1274 * <p> 1275 * A = audio active, T = video transmission active, R = video reception active, P = video 1276 * paused. 1277 * 1278 * @param videoState The video state. 1279 * @return A string indicating which bits are set in the video state. 1280 */ getVideoStateDescription(int videoState)1281 private String getVideoStateDescription(int videoState) { 1282 StringBuilder sb = new StringBuilder(); 1283 sb.append("A"); 1284 1285 if (VideoProfile.isTransmissionEnabled(videoState)) { 1286 sb.append("T"); 1287 } 1288 1289 if (VideoProfile.isReceptionEnabled(videoState)) { 1290 sb.append("R"); 1291 } 1292 1293 if (VideoProfile.isPaused(videoState)) { 1294 sb.append("P"); 1295 } 1296 1297 return sb.toString(); 1298 } 1299 1300 @Override getConnectionServiceWrapper()1301 public ConnectionServiceFocusManager.ConnectionServiceFocus getConnectionServiceWrapper() { 1302 return (!mIsTransactionalCall ? mConnectionService : mTransactionalService); 1303 } 1304 getState()1305 public int getState() { 1306 return mState; 1307 } 1308 1309 /** 1310 * Similar to {@link #getState()}, except will return {@link CallState#DISCONNECTING} if the 1311 * call is locally disconnecting. This is the call state which is reported to the 1312 * {@link android.telecom.InCallService}s when a call is parcelled. 1313 * @return The parcelable call state. 1314 */ getParcelableCallState()1315 public int getParcelableCallState() { 1316 if (isLocallyDisconnecting() && 1317 (mState != android.telecom.Call.STATE_DISCONNECTED)) { 1318 return CallState.DISCONNECTING; 1319 } 1320 return mState; 1321 } 1322 1323 /** 1324 * Determines if this {@link Call} can receive call focus via the 1325 * {@link ConnectionServiceFocusManager}. 1326 * Only top-level calls and non-external calls are eligible. 1327 * @return {@code true} if this call is focusable, {@code false} otherwise. 1328 */ 1329 @Override isFocusable()1330 public boolean isFocusable() { 1331 boolean isChild = getParentCall() != null; 1332 return !isChild && !isExternalCall(); 1333 } 1334 shouldContinueProcessingAfterDisconnect()1335 private boolean shouldContinueProcessingAfterDisconnect() { 1336 // Stop processing once the call is active. 1337 if (!CreateConnectionTimeout.isCallBeingPlaced(this)) { 1338 return false; 1339 } 1340 1341 // Only Redial a Call in the case of it being an Emergency Call. 1342 if(!isEmergencyCall()) { 1343 return false; 1344 } 1345 1346 // Make sure that there are additional connection services to process. 1347 if (mCreateConnectionProcessor == null 1348 || !mCreateConnectionProcessor.isProcessingComplete() 1349 || !mCreateConnectionProcessor.hasMorePhoneAccounts()) { 1350 return false; 1351 } 1352 1353 if (mDisconnectCause == null) { 1354 return false; 1355 } 1356 1357 // Continue processing if the current attempt failed or timed out. 1358 return mDisconnectCause.getCode() == DisconnectCause.ERROR || 1359 mCreateConnectionProcessor.isCallTimedOut(); 1360 } 1361 1362 /** 1363 * Returns the unique ID for this call as it exists in Telecom. 1364 * @return The call ID. 1365 */ getId()1366 public String getId() { 1367 return mId; 1368 } 1369 1370 /** 1371 * Returns the unique ID for this call (see {@link #getId}) along with an attempt indicator that 1372 * iterates based on attempts to establish a {@link Connection} using createConnectionProcessor. 1373 * @return The call ID with an appended attempt id. 1374 */ getConnectionId()1375 public String getConnectionId() { 1376 if(mCreateConnectionProcessor != null) { 1377 mConnectionId = mId + "_" + 1378 String.valueOf(mCreateConnectionProcessor.getConnectionAttempt()); 1379 return mConnectionId; 1380 } else { 1381 return mConnectionId; 1382 } 1383 } 1384 1385 /** 1386 * Handles an incoming overridden disconnect message for this call. 1387 * 1388 * We only care if the disconnect is handled via a future. 1389 * @param message the overridden disconnect message. 1390 */ handleOverrideDisconnectMessage(@ullable CharSequence message)1391 public void handleOverrideDisconnectMessage(@Nullable CharSequence message) { 1392 Log.i(this, "handleOverrideDisconnectMessage; callid=%s, msg=%s", getId(), message); 1393 1394 if (isDisconnectHandledViaFuture()) { 1395 mReceivedCallDiagnosticPostCallResponse = true; 1396 if (message != null) { 1397 Log.addEvent(this, LogUtils.Events.OVERRIDE_DISCONNECT_MESSAGE, message); 1398 // Replace the existing disconnect cause in this call 1399 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.ERROR, message, 1400 message, null)); 1401 } 1402 1403 mDiagnosticCompleteFuture.complete(true); 1404 } else { 1405 Log.w(this, "handleOverrideDisconnectMessage; callid=%s - got override when unbound", 1406 getId()); 1407 } 1408 } 1409 1410 /** 1411 * Sets the call state. Although there exists the notion of appropriate state transitions 1412 * (see {@link CallState}), in practice those expectations break down when cellular systems 1413 * misbehave and they do this very often. The result is that we do not enforce state transitions 1414 * and instead keep the code resilient to unexpected state changes. 1415 * @return true indicates if setState succeeded in setting the state to newState, 1416 * else it is failed, and the call is still in its original state. 1417 */ setState(int newState, String tag)1418 public boolean setState(int newState, String tag) { 1419 if (mState != newState) { 1420 Log.v(this, "setState %s -> %s", CallState.toString(mState), 1421 CallState.toString(newState)); 1422 1423 if (newState == CallState.DISCONNECTED && shouldContinueProcessingAfterDisconnect()) { 1424 Log.w(this, "continuing processing disconnected call with another service"); 1425 if (mFlags.cancelRemovalOnEmergencyRedial() && isDisconnectHandledViaFuture() 1426 && isRemovalPending()) { 1427 Log.i(this, "cancelling removal future in favor of " 1428 + "CreateConnectionProcessor handling removal"); 1429 mRemovalFuture.cancel(true); 1430 } 1431 mCreateConnectionProcessor.continueProcessingIfPossible(this, mDisconnectCause); 1432 return false; 1433 } else if (newState == CallState.ANSWERED && mState == CallState.ACTIVE) { 1434 Log.w(this, "setState %s -> %s; call already active.", CallState.toString(mState), 1435 CallState.toString(newState)); 1436 return false; 1437 } 1438 1439 updateVideoHistoryViaState(mState, newState); 1440 1441 mState = newState; 1442 maybeLoadCannedSmsResponses(); 1443 1444 if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) { 1445 if (mConnectTimeMillis == 0) { 1446 // We check to see if mConnectTime is already set to prevent the 1447 // call from resetting active time when it goes in and out of 1448 // ACTIVE/ON_HOLD 1449 mConnectTimeMillis = mClockProxy.currentTimeMillis(); 1450 mConnectElapsedTimeMillis = mClockProxy.elapsedRealtime(); 1451 mAnalytics.setCallStartTime(mConnectTimeMillis); 1452 } 1453 1454 // We're clearly not disconnected, so reset the disconnected time. 1455 mDisconnectTimeMillis = 0; 1456 mDisconnectElapsedTimeMillis = 0; 1457 mHasGoneActiveBefore = true; 1458 } else if (mState == CallState.DISCONNECTED) { 1459 mDisconnectTimeMillis = mClockProxy.currentTimeMillis(); 1460 mDisconnectElapsedTimeMillis = mClockProxy.elapsedRealtime(); 1461 mAnalytics.setCallEndTime(mDisconnectTimeMillis); 1462 setLocallyDisconnecting(false); 1463 fixParentAfterDisconnect(); 1464 } 1465 1466 // Log the state transition event 1467 String event = null; 1468 Object data = null; 1469 switch (newState) { 1470 case CallState.ACTIVE: 1471 event = LogUtils.Events.SET_ACTIVE; 1472 break; 1473 case CallState.CONNECTING: 1474 event = LogUtils.Events.SET_CONNECTING; 1475 break; 1476 case CallState.DIALING: 1477 event = LogUtils.Events.SET_DIALING; 1478 break; 1479 case CallState.PULLING: 1480 event = LogUtils.Events.SET_PULLING; 1481 break; 1482 case CallState.DISCONNECTED: 1483 event = LogUtils.Events.SET_DISCONNECTED; 1484 data = getDisconnectCause(); 1485 break; 1486 case CallState.DISCONNECTING: 1487 event = LogUtils.Events.SET_DISCONNECTING; 1488 break; 1489 case CallState.ON_HOLD: 1490 event = LogUtils.Events.SET_HOLD; 1491 break; 1492 case CallState.SELECT_PHONE_ACCOUNT: 1493 event = LogUtils.Events.SET_SELECT_PHONE_ACCOUNT; 1494 break; 1495 case CallState.RINGING: 1496 event = LogUtils.Events.SET_RINGING; 1497 break; 1498 case CallState.ANSWERED: 1499 event = LogUtils.Events.SET_ANSWERED; 1500 break; 1501 case CallState.AUDIO_PROCESSING: 1502 event = LogUtils.Events.SET_AUDIO_PROCESSING; 1503 break; 1504 case CallState.SIMULATED_RINGING: 1505 event = LogUtils.Events.SET_SIMULATED_RINGING; 1506 break; 1507 } 1508 if (event != null) { 1509 // The string data should be just the tag. 1510 String stringData = tag; 1511 if (data != null) { 1512 // If data exists, add it to tag. If no tag, just use data.toString(). 1513 stringData = stringData == null ? data.toString() : stringData + "> " + data; 1514 } 1515 Log.addEvent(this, event, stringData); 1516 } 1517 1518 if (mFlags.transactionalCsVerifier()) { 1519 for (CallStateListener listener : mCallStateListeners) { 1520 listener.onCallStateChanged(newState); 1521 } 1522 } 1523 1524 mCallStateChangedAtomWriter 1525 .setDisconnectCause(getDisconnectCause()) 1526 .setSelfManaged(isSelfManaged()) 1527 .setExternalCall(isExternalCall()) 1528 .setEmergencyCall(isEmergencyCall()) 1529 .setDurationSeconds(Long.valueOf( 1530 (mDisconnectTimeMillis - mConnectTimeMillis) / 1000).intValue()) 1531 .write(newState); 1532 } 1533 return true; 1534 } 1535 setRingbackRequested(boolean ringbackRequested)1536 void setRingbackRequested(boolean ringbackRequested) { 1537 mRingbackRequested = ringbackRequested; 1538 for (Listener l : mListeners) { 1539 l.onRingbackRequested(this, mRingbackRequested); 1540 } 1541 } 1542 isRingbackRequested()1543 public boolean isRingbackRequested() { 1544 return mRingbackRequested; 1545 } 1546 setSilentRingingRequested(boolean silentRingingRequested)1547 public void setSilentRingingRequested(boolean silentRingingRequested) { 1548 mSilentRingingRequested = silentRingingRequested; 1549 Bundle bundle = new Bundle(); 1550 bundle.putBoolean(android.telecom.Call.EXTRA_SILENT_RINGING_REQUESTED, 1551 silentRingingRequested); 1552 putConnectionServiceExtras(bundle); 1553 } 1554 isSilentRingingRequested()1555 public boolean isSilentRingingRequested() { 1556 return mSilentRingingRequested; 1557 } 1558 setCallIsSuppressedByDoNotDisturb(boolean isCallSuppressed)1559 public void setCallIsSuppressedByDoNotDisturb(boolean isCallSuppressed) { 1560 Bundle bundle = new Bundle(); 1561 bundle.putBoolean(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB, 1562 isCallSuppressed); 1563 putConnectionServiceExtras(bundle); 1564 } 1565 isCallSuppressedByDoNotDisturb()1566 public boolean isCallSuppressedByDoNotDisturb() { 1567 if (getExtras() == null) { 1568 return false; 1569 } 1570 return getExtras().getBoolean(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB); 1571 } 1572 wasDndCheckComputedForCall()1573 public boolean wasDndCheckComputedForCall() { 1574 if (getExtras() == null) { 1575 return false; 1576 } 1577 return getExtras().containsKey(android.telecom.Call.EXTRA_IS_SUPPRESSED_BY_DO_NOT_DISTURB); 1578 } 1579 1580 @VisibleForTesting isConference()1581 public boolean isConference() { 1582 return mIsConference; 1583 } 1584 1585 /** 1586 * @return {@code true} if this call had children at some point, {@code false} otherwise. 1587 */ hadChildren()1588 public boolean hadChildren() { 1589 return mHadChildren; 1590 } 1591 getHandle()1592 public Uri getHandle() { 1593 return mHandle; 1594 } 1595 getParticipants()1596 public List<Uri> getParticipants() { 1597 return mParticipants; 1598 } 1599 isAdhocConferenceCall()1600 public boolean isAdhocConferenceCall() { 1601 return mIsConference && 1602 (mCallDirection == CALL_DIRECTION_OUTGOING || 1603 mCallDirection == CALL_DIRECTION_INCOMING); 1604 } 1605 getPostDialDigits()1606 public String getPostDialDigits() { 1607 return mPostDialDigits; 1608 } 1609 clearPostDialDigits()1610 public void clearPostDialDigits() { 1611 mPostDialDigits = null; 1612 } 1613 getViaNumber()1614 public String getViaNumber() { 1615 return mViaNumber; 1616 } 1617 setViaNumber(String viaNumber)1618 public void setViaNumber(String viaNumber) { 1619 // If at any point the via number is not empty throughout the call, save that via number. 1620 if (!TextUtils.isEmpty(viaNumber)) { 1621 mViaNumber = viaNumber; 1622 } 1623 } 1624 getHandlePresentation()1625 public int getHandlePresentation() { 1626 return mHandlePresentation; 1627 } 1628 setCallerNumberVerificationStatus( @onnection.VerificationStatus int callerNumberVerificationStatus)1629 public void setCallerNumberVerificationStatus( 1630 @Connection.VerificationStatus int callerNumberVerificationStatus) { 1631 mCallerNumberVerificationStatus = callerNumberVerificationStatus; 1632 mListeners.forEach(l -> l.onCallerNumberVerificationStatusChanged(this, 1633 callerNumberVerificationStatus)); 1634 } 1635 getCallerNumberVerificationStatus()1636 public @Connection.VerificationStatus int getCallerNumberVerificationStatus() { 1637 return mCallerNumberVerificationStatus; 1638 } 1639 setHandle(Uri handle)1640 void setHandle(Uri handle) { 1641 setHandle(handle, TelecomManager.PRESENTATION_ALLOWED); 1642 } 1643 setHandle(Uri handle, int presentation)1644 public void setHandle(Uri handle, int presentation) { 1645 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) { 1646 mHandlePresentation = presentation; 1647 if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED || 1648 mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) { 1649 mHandle = null; 1650 } else { 1651 mHandle = handle; 1652 if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme()) 1653 && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) { 1654 // If the number is actually empty, set it to null, unless this is a 1655 // SCHEME_VOICEMAIL uri which always has an empty number. 1656 mHandle = null; 1657 } 1658 } 1659 1660 // Let's not allow resetting of the emergency flag. Once a call becomes an emergency 1661 // call, it will remain so for the rest of it's lifetime. 1662 if (!mIsEmergencyCall) { 1663 try { 1664 mIsEmergencyCall = mHandle != null && 1665 getTelephonyManager().isEmergencyNumber( 1666 mHandle.getSchemeSpecificPart()); 1667 } catch (UnsupportedOperationException use) { 1668 Log.i(this, "setHandle: no FEATURE_TELEPHONY; emergency state unknown."); 1669 mIsEmergencyCall = false; 1670 } catch (IllegalStateException ise) { 1671 Log.e(this, ise, "setHandle: can't determine if number is emergency"); 1672 mIsEmergencyCall = false; 1673 } catch (RuntimeException r) { 1674 Log.e(this, r, "setHandle: can't determine if number is emergency"); 1675 mIsEmergencyCall = false; 1676 } 1677 mAnalytics.setCallIsEmergency(mIsEmergencyCall); 1678 } 1679 if (!mIsTestEmergencyCall) { 1680 mIsTestEmergencyCall = mHandle != null && 1681 isTestEmergencyCall(mHandle.getSchemeSpecificPart()); 1682 } 1683 if (mTargetPhoneAccountHandle == null || !mContext.getResources().getString( 1684 R.string.skip_incoming_caller_info_account_package).equalsIgnoreCase( 1685 mTargetPhoneAccountHandle.getComponentName().getPackageName())) { 1686 startCallerInfoLookup(); 1687 } else { 1688 Log.i(this, "skip incoming caller info lookup"); 1689 } 1690 for (Listener l : mListeners) { 1691 l.onHandleChanged(this); 1692 } 1693 } 1694 } 1695 isTestEmergencyCall(String number)1696 private boolean isTestEmergencyCall(String number) { 1697 try { 1698 Map<Integer, List<EmergencyNumber>> eMap = 1699 getTelephonyManager().getEmergencyNumberList(); 1700 return eMap.values().stream().flatMap(Collection::stream) 1701 .anyMatch(eNumber -> 1702 eNumber.isFromSources(EmergencyNumber.EMERGENCY_NUMBER_SOURCE_TEST) && 1703 number.equals(eNumber.getNumber())); 1704 } catch (UnsupportedOperationException uoe) { 1705 // No Telephony feature, so unable to determine. 1706 return false; 1707 } catch (IllegalStateException ise) { 1708 return false; 1709 } catch (RuntimeException r) { 1710 return false; 1711 } 1712 } 1713 getContactPhotoUri()1714 public Uri getContactPhotoUri() { 1715 return mCallerInfo != null ? mCallerInfo.getContactDisplayPhotoUri() : null; 1716 } 1717 getCallerDisplayName()1718 public String getCallerDisplayName() { 1719 return mCallerDisplayName; 1720 } 1721 getCallerDisplayNamePresentation()1722 public int getCallerDisplayNamePresentation() { 1723 return mCallerDisplayNamePresentation; 1724 } 1725 setCallerDisplayName(String callerDisplayName, int presentation)1726 void setCallerDisplayName(String callerDisplayName, int presentation) { 1727 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) || 1728 presentation != mCallerDisplayNamePresentation) { 1729 mCallerDisplayName = callerDisplayName; 1730 mCallerDisplayNamePresentation = presentation; 1731 for (Listener l : mListeners) { 1732 l.onCallerDisplayNameChanged(this); 1733 } 1734 } 1735 } 1736 setContactPhotoUri(Uri contactPhotoUri)1737 void setContactPhotoUri(Uri contactPhotoUri) { 1738 if (mCallerInfo != null) { 1739 mCallerInfo.SetContactDisplayPhotoUri(contactPhotoUri); 1740 } 1741 } 1742 getName()1743 public String getName() { 1744 return mCallerInfo == null ? null : mCallerInfo.getName(); 1745 } 1746 getPhoneNumber()1747 public String getPhoneNumber() { 1748 return mCallerInfo == null ? null : mCallerInfo.getPhoneNumber(); 1749 } 1750 getPhotoIcon()1751 public Bitmap getPhotoIcon() { 1752 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon; 1753 } 1754 getPhoto()1755 public Drawable getPhoto() { 1756 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto; 1757 } 1758 1759 /** 1760 * @param cause The reason for the disconnection, represented by 1761 * {@link android.telecom.DisconnectCause}. 1762 */ setDisconnectCause(DisconnectCause cause)1763 public void setDisconnectCause(DisconnectCause cause) { 1764 // TODO: Consider combining this method with a setDisconnected() method that is totally 1765 // separate from setState. 1766 1767 if (mOverrideDisconnectCause.getCode() != DisconnectCause.UNKNOWN) { 1768 cause = new DisconnectCause(mOverrideDisconnectCause.getCode(), 1769 TextUtils.isEmpty(mOverrideDisconnectCause.getLabel()) ? 1770 cause.getLabel() : mOverrideDisconnectCause.getLabel(), 1771 (mOverrideDisconnectCause.getDescription() == null) ? 1772 cause.getDescription() :mOverrideDisconnectCause.getDescription(), 1773 TextUtils.isEmpty(mOverrideDisconnectCause.getReason()) ? 1774 cause.getReason() : mOverrideDisconnectCause.getReason(), 1775 (mOverrideDisconnectCause.getTone() == 0) ? 1776 cause.getTone() : mOverrideDisconnectCause.getTone()); 1777 } 1778 mAnalytics.setCallDisconnectCause(cause); 1779 mDisconnectCause = cause; 1780 } 1781 setOverrideDisconnectCauseCode(DisconnectCause overrideDisconnectCause)1782 public void setOverrideDisconnectCauseCode(DisconnectCause overrideDisconnectCause) { 1783 mOverrideDisconnectCause = overrideDisconnectCause; 1784 } 1785 1786 getDisconnectCause()1787 public DisconnectCause getDisconnectCause() { 1788 return mDisconnectCause; 1789 } 1790 1791 /** 1792 * @return {@code true} if this is an outgoing call to emergency services. An outgoing call is 1793 * identified as an emergency call by the dialer phone number. 1794 */ isEmergencyCall()1795 public boolean isEmergencyCall() { 1796 return mIsEmergencyCall; 1797 } 1798 1799 /** 1800 * For testing purposes, set if this call is an emergency call or not. 1801 * @param isEmergencyCall {@code true} if emergency, {@code false} otherwise. 1802 */ 1803 @VisibleForTesting setIsEmergencyCall(boolean isEmergencyCall)1804 public void setIsEmergencyCall(boolean isEmergencyCall) { 1805 mIsEmergencyCall = isEmergencyCall; 1806 } 1807 1808 /** 1809 * @return {@code true} if this an outgoing call to a test emergency number (and NOT to 1810 * emergency services). Used for testing purposes to differentiate between a real and fake 1811 * emergency call for safety reasons during testing. 1812 */ isTestEmergencyCall()1813 public boolean isTestEmergencyCall() { 1814 return mIsTestEmergencyCall; 1815 } 1816 1817 /** 1818 * @return {@code true} if the target phone account is in ECBM. 1819 */ isInECBM()1820 public boolean isInECBM() { 1821 return mIsInECBM; 1822 } 1823 1824 /** 1825 * Set if the target phone account is in ECBM. 1826 * @param isInEcbm {@code true} if target phone account is in ECBM, {@code false} otherwise. 1827 */ setIsInECBM(boolean isInECBM)1828 public void setIsInECBM(boolean isInECBM) { 1829 mIsInECBM = isInECBM; 1830 } 1831 1832 /** 1833 * @return {@code true} if the network has identified this call as an emergency call. 1834 */ isNetworkIdentifiedEmergencyCall()1835 public boolean isNetworkIdentifiedEmergencyCall() { 1836 return hasProperty(Connection.PROPERTY_NETWORK_IDENTIFIED_EMERGENCY_CALL); 1837 } 1838 1839 /** 1840 * @return The original handle this call is associated with. In-call services should use this 1841 * handle when indicating in their UI the handle that is being called. 1842 */ getOriginalHandle()1843 public Uri getOriginalHandle() { 1844 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) { 1845 return mGatewayInfo.getOriginalAddress(); 1846 } 1847 return getHandle(); 1848 } 1849 1850 @VisibleForTesting getGatewayInfo()1851 public GatewayInfo getGatewayInfo() { 1852 return mGatewayInfo; 1853 } 1854 setGatewayInfo(GatewayInfo gatewayInfo)1855 void setGatewayInfo(GatewayInfo gatewayInfo) { 1856 mGatewayInfo = gatewayInfo; 1857 } 1858 1859 @VisibleForTesting getConnectionManagerPhoneAccount()1860 public PhoneAccountHandle getConnectionManagerPhoneAccount() { 1861 return mConnectionManagerPhoneAccountHandle; 1862 } 1863 1864 @VisibleForTesting setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle)1865 public void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) { 1866 if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) { 1867 mConnectionManagerPhoneAccountHandle = accountHandle; 1868 for (Listener l : mListeners) { 1869 l.onConnectionManagerPhoneAccountChanged(this); 1870 } 1871 } 1872 checkIfRttCapable(); 1873 } 1874 1875 /** 1876 * @return the {@link PhoneAccountHandle} of the remote connection service which placing this 1877 * call was delegated to, or {@code null} if a remote connection service was not used. 1878 */ getRemotePhoneAccountHandle()1879 public @Nullable PhoneAccountHandle getRemotePhoneAccountHandle() { 1880 return mRemotePhoneAccountHandle; 1881 } 1882 1883 /** 1884 * Sets the {@link PhoneAccountHandle} of the remote connection service which placing this 1885 * call was delegated to. 1886 * @param accountHandle The phone account handle. 1887 */ setRemotePhoneAccountHandle(PhoneAccountHandle accountHandle)1888 public void setRemotePhoneAccountHandle(PhoneAccountHandle accountHandle) { 1889 mRemotePhoneAccountHandle = accountHandle; 1890 } 1891 1892 /** 1893 * Determines which {@link PhoneAccountHandle} is actually placing a call. 1894 * Where {@link #getRemotePhoneAccountHandle()} is non-null, the connection manager is placing 1895 * the call via a remote connection service, so the remote connection service's phone account 1896 * is the source. 1897 * Where {@link #getConnectionManagerPhoneAccount()} is non-null and 1898 * {@link #getRemotePhoneAccountHandle()} is null, the connection manager is placing the call 1899 * itself (even if the target specifies something else). 1900 * Finally, if neither of the above cases apply, the target phone account is the one actually 1901 * placing the call. 1902 * @return The {@link PhoneAccountHandle} which is actually placing a call. 1903 */ getDelegatePhoneAccountHandle()1904 public @NonNull PhoneAccountHandle getDelegatePhoneAccountHandle() { 1905 if (mRemotePhoneAccountHandle != null) { 1906 return mRemotePhoneAccountHandle; 1907 } 1908 if (mConnectionManagerPhoneAccountHandle != null) { 1909 return mConnectionManagerPhoneAccountHandle; 1910 } 1911 return mTargetPhoneAccountHandle; 1912 } 1913 getTargetPhoneAccount()1914 public PhoneAccountHandle getTargetPhoneAccount() { 1915 return mTargetPhoneAccountHandle; 1916 } 1917 1918 @VisibleForTesting setTargetPhoneAccount(PhoneAccountHandle accountHandle)1919 public void setTargetPhoneAccount(PhoneAccountHandle accountHandle) { 1920 if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) { 1921 mTargetPhoneAccountHandle = accountHandle; 1922 // Update the last MO emergency call in the helper, if applicable. 1923 if (isEmergencyCall() && !isIncoming()) { 1924 mCallsManager.getEmergencyCallHelper().setLastOutgoingEmergencyCallPAH( 1925 accountHandle); 1926 } 1927 for (Listener l : mListeners) { 1928 l.onTargetPhoneAccountChanged(this); 1929 } 1930 configureCallAttributes(); 1931 } 1932 checkIfVideoCapable(); 1933 checkIfRttCapable(); 1934 1935 if (accountHandle != null) { 1936 mCallStateChangedAtomWriter.setUid( 1937 accountHandle.getComponentName().getPackageName(), 1938 mContext.getPackageManager()); 1939 // Set the associated user for the call for MT calls based on the target phone account. 1940 UserHandle associatedUser = UserUtil.getAssociatedUserForCall( 1941 mFlags.associatedUserRefactorForWorkProfile(), 1942 mCallsManager.getPhoneAccountRegistrar(), mCallsManager.getCurrentUserHandle(), 1943 accountHandle); 1944 if (isIncoming() && !associatedUser.equals(mAssociatedUser)) { 1945 setAssociatedUser(associatedUser); 1946 } 1947 } 1948 } 1949 getPhoneAccountFromHandle()1950 public PhoneAccount getPhoneAccountFromHandle() { 1951 if (getTargetPhoneAccount() == null) { 1952 return null; 1953 } 1954 PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar() 1955 .getPhoneAccountUnchecked(getTargetPhoneAccount()); 1956 1957 if (phoneAccount == null) { 1958 return null; 1959 } 1960 1961 return phoneAccount; 1962 } 1963 getTargetPhoneAccountLabel()1964 public CharSequence getTargetPhoneAccountLabel() { 1965 if (getTargetPhoneAccount() == null) { 1966 return null; 1967 } 1968 PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar() 1969 .getPhoneAccountUnchecked(getTargetPhoneAccount()); 1970 1971 if (phoneAccount == null) { 1972 return null; 1973 } 1974 1975 return phoneAccount.getLabel(); 1976 } 1977 1978 /** 1979 * Determines if this Call should be written to the call log. 1980 * @return {@code true} for managed calls or for self-managed calls which have the 1981 * {@link PhoneAccount#EXTRA_LOG_SELF_MANAGED_CALLS} extra set. 1982 */ isLoggedSelfManaged()1983 public boolean isLoggedSelfManaged() { 1984 if (!isSelfManaged()) { 1985 // Managed calls are always logged. 1986 return true; 1987 } 1988 if (getTargetPhoneAccount() == null) { 1989 return false; 1990 } 1991 PhoneAccount phoneAccount = mCallsManager.getPhoneAccountRegistrar() 1992 .getPhoneAccountUnchecked(getTargetPhoneAccount()); 1993 1994 if (phoneAccount == null) { 1995 return false; 1996 } 1997 1998 if (getHandle() == null) { 1999 // No point in logging a null-handle call. Some self-managed calls will have this. 2000 return false; 2001 } 2002 2003 if (!PhoneAccount.SCHEME_SIP.equals(getHandle().getScheme()) && 2004 !PhoneAccount.SCHEME_TEL.equals(getHandle().getScheme())) { 2005 // Can't log schemes other than SIP or TEL for now. 2006 return false; 2007 } 2008 2009 return phoneAccount.getExtras() != null && phoneAccount.getExtras().getBoolean( 2010 PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, false); 2011 } 2012 isIncoming()2013 public boolean isIncoming() { 2014 return mCallDirection == CALL_DIRECTION_INCOMING; 2015 } 2016 isExternalCall()2017 public boolean isExternalCall() { 2018 return (getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 2019 Connection.PROPERTY_IS_EXTERNAL_CALL; 2020 } 2021 isWorkCall()2022 public boolean isWorkCall() { 2023 return mIsWorkCall; 2024 } 2025 isUsingCallRecordingTone()2026 public boolean isUsingCallRecordingTone() { 2027 return mUseCallRecordingTone; 2028 } 2029 2030 /** 2031 * @return {@code true} if the {@link Call}'s {@link #getTargetPhoneAccount()} supports video. 2032 */ isVideoCallingSupportedByPhoneAccount()2033 public boolean isVideoCallingSupportedByPhoneAccount() { 2034 return mIsVideoCallingSupportedByPhoneAccount; 2035 } 2036 2037 /** 2038 * Sets whether video calling is supported by the current phone account. Since video support 2039 * can change during a call, this method facilitates updating call video state. 2040 * @param isVideoCallingSupported Sets whether video calling is supported. 2041 */ setVideoCallingSupportedByPhoneAccount(boolean isVideoCallingSupported)2042 public void setVideoCallingSupportedByPhoneAccount(boolean isVideoCallingSupported) { 2043 if (mIsVideoCallingSupportedByPhoneAccount == isVideoCallingSupported) { 2044 return; 2045 } 2046 Log.i(this, "setVideoCallingSupportedByPhoneAccount: isSupp=%b", isVideoCallingSupported); 2047 mIsVideoCallingSupportedByPhoneAccount = isVideoCallingSupported; 2048 2049 // Force an update of the connection capabilities so that the dialer is informed of the new 2050 // video capabilities based on the phone account's support for video. 2051 setConnectionCapabilities(getConnectionCapabilities(), true /* force */); 2052 } 2053 2054 /** 2055 * Determines if pulling this external call is supported. If it is supported, we will allow the 2056 * {@link Connection#CAPABILITY_CAN_PULL_CALL} capability to be added to this call's 2057 * capabilities. If it is not supported, we will strip this capability before sending this 2058 * call's capabilities to the InCallService. 2059 * @param isPullExternalCallSupported true, if pulling this external call is supported, false 2060 * otherwise. 2061 */ setIsPullExternalCallSupported(boolean isPullExternalCallSupported)2062 public void setIsPullExternalCallSupported(boolean isPullExternalCallSupported) { 2063 if (!isExternalCall()) return; 2064 if (isPullExternalCallSupported == mIsPullExternalCallSupported) return; 2065 2066 Log.i(this, "setCanPullExternalCall: canPull=%b", isPullExternalCallSupported); 2067 2068 mIsPullExternalCallSupported = isPullExternalCallSupported; 2069 2070 // Use mConnectionCapabilities here to get the unstripped capabilities. 2071 setConnectionCapabilities(mConnectionCapabilities, true /* force */); 2072 } 2073 2074 /** 2075 * @return {@code true} if the {@link Call} locally supports video. 2076 */ isLocallyVideoCapable()2077 public boolean isLocallyVideoCapable() { 2078 return (getConnectionCapabilities() & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) 2079 == Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL; 2080 } 2081 isSelfManaged()2082 public boolean isSelfManaged() { 2083 return mIsSelfManaged; 2084 } 2085 setIsSelfManaged(boolean isSelfManaged)2086 public void setIsSelfManaged(boolean isSelfManaged) { 2087 mIsSelfManaged = isSelfManaged; 2088 2089 // Connection properties will add/remove the PROPERTY_SELF_MANAGED. 2090 setConnectionProperties(getConnectionProperties()); 2091 } 2092 isTransactionalCall()2093 public boolean isTransactionalCall() { 2094 return mIsTransactionalCall; 2095 } 2096 setIsTransactionalCall(boolean isTransactionalCall)2097 public void setIsTransactionalCall(boolean isTransactionalCall) { 2098 mIsTransactionalCall = isTransactionalCall; 2099 2100 // Connection properties will add/remove the PROPERTY_SELF_MANAGED. 2101 setConnectionProperties(getConnectionProperties()); 2102 } 2103 setCallingPackageIdentity(Bundle extras)2104 public void setCallingPackageIdentity(Bundle extras) { 2105 mCallingPackageIdentity = new CallingPackageIdentity(extras); 2106 // These extras should NOT be propagated to Dialer and should be removed. 2107 extras.remove(CallAttributes.CALLER_PID_KEY); 2108 extras.remove(CallAttributes.CALLER_UID_KEY); 2109 } 2110 getCallingPackageIdentity()2111 public CallingPackageIdentity getCallingPackageIdentity() { 2112 return mCallingPackageIdentity; 2113 } 2114 setTransactionServiceWrapper(TransactionalServiceWrapper service)2115 public void setTransactionServiceWrapper(TransactionalServiceWrapper service) { 2116 Log.i(this, "setTransactionServiceWrapper: service=[%s]", service); 2117 mTransactionalService = service; 2118 processCachedCallbacks(service); 2119 } 2120 processCachedCallbacks(CallSourceService service)2121 private void processCachedCallbacks(CallSourceService service) { 2122 if(mFlags.cacheCallAudioCallbacks()) { 2123 synchronized (mCachedServiceCallbacks) { 2124 for (List<CachedCallback> callbacks : mCachedServiceCallbacks.values()) { 2125 callbacks.forEach( callback -> callback.executeCallback(service, this)); 2126 } 2127 // clear list for memory cleanup purposes. The Service should never be reset 2128 mCachedServiceCallbacks.clear(); 2129 } 2130 } 2131 } 2132 getService()2133 public CallSourceService getService() { 2134 if (isTransactionalCall()) { 2135 return mTransactionalService; 2136 } else { 2137 return mConnectionService; 2138 } 2139 } 2140 getTransactionServiceWrapper()2141 public TransactionalServiceWrapper getTransactionServiceWrapper() { 2142 return mTransactionalService; 2143 } 2144 visibleToInCallService()2145 public boolean visibleToInCallService() { 2146 return mVisibleToInCallService; 2147 } 2148 setVisibleToInCallService(boolean visibleToInCallService)2149 public void setVisibleToInCallService(boolean visibleToInCallService) { 2150 mVisibleToInCallService = visibleToInCallService; 2151 } 2152 markFinishedHandoverStateAndCleanup(int handoverState)2153 public void markFinishedHandoverStateAndCleanup(int handoverState) { 2154 if (mHandoverSourceCall != null) { 2155 mHandoverSourceCall.setHandoverState(handoverState); 2156 } else if (mHandoverDestinationCall != null) { 2157 mHandoverDestinationCall.setHandoverState(handoverState); 2158 } 2159 setHandoverState(handoverState); 2160 maybeCleanupHandover(); 2161 } 2162 maybeCleanupHandover()2163 public void maybeCleanupHandover() { 2164 if (mHandoverSourceCall != null) { 2165 mHandoverSourceCall.setHandoverSourceCall(null); 2166 mHandoverSourceCall.setHandoverDestinationCall(null); 2167 mHandoverSourceCall = null; 2168 } else if (mHandoverDestinationCall != null) { 2169 mHandoverDestinationCall.setHandoverSourceCall(null); 2170 mHandoverDestinationCall.setHandoverDestinationCall(null); 2171 mHandoverDestinationCall = null; 2172 } 2173 } 2174 isHandoverInProgress()2175 public boolean isHandoverInProgress() { 2176 return mHandoverSourceCall != null || mHandoverDestinationCall != null; 2177 } 2178 getHandoverDestinationCall()2179 public Call getHandoverDestinationCall() { 2180 return mHandoverDestinationCall; 2181 } 2182 setHandoverDestinationCall(Call call)2183 public void setHandoverDestinationCall(Call call) { 2184 mHandoverDestinationCall = call; 2185 } 2186 getHandoverSourceCall()2187 public Call getHandoverSourceCall() { 2188 return mHandoverSourceCall; 2189 } 2190 setHandoverSourceCall(Call call)2191 public void setHandoverSourceCall(Call call) { 2192 mHandoverSourceCall = call; 2193 } 2194 setHandoverState(int handoverState)2195 public void setHandoverState(int handoverState) { 2196 Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(), 2197 HandoverState.stateToString(handoverState)); 2198 mHandoverState = handoverState; 2199 } 2200 getHandoverState()2201 public int getHandoverState() { 2202 return mHandoverState; 2203 } 2204 configureCallAttributes()2205 private void configureCallAttributes() { 2206 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar(); 2207 boolean isWorkCall = false; 2208 boolean isCallRecordingToneSupported = false; 2209 boolean isSimCall = false; 2210 PhoneAccount phoneAccount = 2211 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle); 2212 if (phoneAccount != null) { 2213 final UserHandle userHandle; 2214 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) { 2215 userHandle = mAssociatedUser; 2216 } else { 2217 userHandle = mTargetPhoneAccountHandle.getUserHandle(); 2218 } 2219 if (userHandle != null) { 2220 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle, mFlags); 2221 } 2222 2223 if (!mFlags.telecomResolveHiddenDependencies()) { 2224 isCallRecordingToneSupported = (phoneAccount.hasCapabilities( 2225 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) 2226 && phoneAccount.getExtras() != null 2227 && phoneAccount.getExtras().getBoolean( 2228 PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false)); 2229 } else { 2230 isCallRecordingToneSupported = false; 2231 } 2232 isSimCall = phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION); 2233 } 2234 mIsWorkCall = isWorkCall; 2235 mUseCallRecordingTone = isCallRecordingToneSupported; 2236 mIsSimCall = isSimCall; 2237 } 2238 2239 /** 2240 * Caches the state of the {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} {@link PhoneAccount} 2241 * capability and ensures that the video state is updated if the phone account does not support 2242 * video calling. 2243 */ checkIfVideoCapable()2244 private void checkIfVideoCapable() { 2245 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar(); 2246 if (mTargetPhoneAccountHandle == null) { 2247 // If no target phone account handle is specified, assume we can potentially perform a 2248 // video call; once the phone account is set, we can confirm that it is video capable. 2249 mIsVideoCallingSupportedByPhoneAccount = true; 2250 Log.d(this, "checkIfVideoCapable: no phone account selected; assume video capable."); 2251 return; 2252 } 2253 PhoneAccount phoneAccount = 2254 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle); 2255 mIsVideoCallingSupportedByPhoneAccount = phoneAccount != null && 2256 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_VIDEO_CALLING); 2257 2258 if (!mIsVideoCallingSupportedByPhoneAccount && VideoProfile.isVideo(getVideoState())) { 2259 // The PhoneAccount for the Call was set to one which does not support video calling, 2260 // and the current call is configured to be a video call; downgrade to audio-only. 2261 setVideoState(VideoProfile.STATE_AUDIO_ONLY); 2262 Log.d(this, "checkIfVideoCapable: selected phone account doesn't support video."); 2263 } 2264 } 2265 checkIfRttCapable()2266 private void checkIfRttCapable() { 2267 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar(); 2268 if (mTargetPhoneAccountHandle == null) { 2269 return; 2270 } 2271 2272 // Check both the target phone account and the connection manager phone account -- if 2273 // either support RTT, just set the streams and have them set/unset the RTT property as 2274 // needed. 2275 PhoneAccount phoneAccount = 2276 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle); 2277 PhoneAccount connectionManagerPhoneAccount = phoneAccountRegistrar.getPhoneAccountUnchecked( 2278 mConnectionManagerPhoneAccountHandle); 2279 boolean isRttSupported = phoneAccount != null && phoneAccount.hasCapabilities( 2280 PhoneAccount.CAPABILITY_RTT); 2281 boolean isConnectionManagerRttSupported = connectionManagerPhoneAccount != null 2282 && connectionManagerPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT); 2283 2284 if ((isConnectionManagerRttSupported || isRttSupported) 2285 && mDidRequestToStartWithRtt && !areRttStreamsInitialized()) { 2286 // If the phone account got set to an RTT capable one and we haven't set the streams 2287 // yet, do so now. 2288 createRttStreams(); 2289 Log.i(this, "Setting RTT streams after target phone account selected"); 2290 } 2291 } 2292 shouldAttachToExistingConnection()2293 boolean shouldAttachToExistingConnection() { 2294 return mShouldAttachToExistingConnection; 2295 } 2296 2297 /** 2298 * Note: This method relies on {@link #mConnectElapsedTimeMillis} and 2299 * {@link #mDisconnectElapsedTimeMillis} which are independent of the wall clock (which could 2300 * change due to clock changes). 2301 * @return The "age" of this call object in milliseconds, which typically also represents the 2302 * period since this call was added to the set pending outgoing calls. 2303 */ getAgeMillis()2304 public long getAgeMillis() { 2305 if (mState == CallState.DISCONNECTED && 2306 (mDisconnectCause.getCode() == DisconnectCause.REJECTED || 2307 mDisconnectCause.getCode() == DisconnectCause.MISSED)) { 2308 // Rejected and missed calls have no age. They're immortal!! 2309 return 0; 2310 } else if (mConnectElapsedTimeMillis == 0) { 2311 // Age is measured in the amount of time the call was active. A zero connect time 2312 // indicates that we never went active, so return 0 for the age. 2313 return 0; 2314 } else if (mDisconnectElapsedTimeMillis == 0) { 2315 // We connected, but have not yet disconnected 2316 return mClockProxy.elapsedRealtime() - mConnectElapsedTimeMillis; 2317 } 2318 2319 return mDisconnectElapsedTimeMillis - mConnectElapsedTimeMillis; 2320 } 2321 2322 /** 2323 * @return The time when this call object was created and added to the set of pending outgoing 2324 * calls. 2325 */ getCreationTimeMillis()2326 public long getCreationTimeMillis() { 2327 return mCreationTimeMillis; 2328 } 2329 2330 /** 2331 * @return The elapsed realtime millis when the call was created; ONLY useful for determining 2332 * how long has elapsed since the call was first created. 2333 */ getCreationElapsedRealtimeMillis()2334 public long getCreationElapsedRealtimeMillis() { 2335 return mCreationElapsedRealtimeMillis; 2336 } 2337 getConnectTimeMillis()2338 public long getConnectTimeMillis() { 2339 return mConnectTimeMillis; 2340 } 2341 setConnectTimeMillis(long connectTimeMillis)2342 public void setConnectTimeMillis(long connectTimeMillis) { 2343 mConnectTimeMillis = connectTimeMillis; 2344 } 2345 setConnectElapsedTimeMillis(long connectElapsedTimeMillis)2346 public void setConnectElapsedTimeMillis(long connectElapsedTimeMillis) { 2347 mConnectElapsedTimeMillis = connectElapsedTimeMillis; 2348 } 2349 getConnectionCapabilities()2350 public int getConnectionCapabilities() { 2351 return stripUnsupportedCapabilities(mConnectionCapabilities); 2352 } 2353 getConnectionProperties()2354 int getConnectionProperties() { 2355 return mConnectionProperties; 2356 } 2357 setConnectionCapabilities(int connectionCapabilities)2358 public void setConnectionCapabilities(int connectionCapabilities) { 2359 setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */); 2360 } 2361 setTransactionalCapabilities(Bundle extras)2362 public void setTransactionalCapabilities(Bundle extras) { 2363 if (!mFlags.remapTransactionalCapabilities()) { 2364 setConnectionCapabilities( 2365 extras.getInt(CallAttributes.CALL_CAPABILITIES_KEY, 2366 CallAttributes.SUPPORTS_SET_INACTIVE), true); 2367 return; 2368 } 2369 int connectionCapabilitesBitmap = 0; 2370 int transactionalCapabilitiesBitmap = extras.getInt( 2371 CallAttributes.CALL_CAPABILITIES_KEY, 2372 CallAttributes.SUPPORTS_SET_INACTIVE); 2373 if ((transactionalCapabilitiesBitmap & CallAttributes.SUPPORTS_SET_INACTIVE) 2374 == CallAttributes.SUPPORTS_SET_INACTIVE) { 2375 connectionCapabilitesBitmap = connectionCapabilitesBitmap | Connection.CAPABILITY_HOLD 2376 | Connection.CAPABILITY_SUPPORT_HOLD; 2377 } 2378 setConnectionCapabilities(connectionCapabilitesBitmap, true); 2379 } 2380 setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate)2381 void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) { 2382 Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString( 2383 connectionCapabilities)); 2384 if (forceUpdate || mConnectionCapabilities != connectionCapabilities) { 2385 int previousCapabilities = mConnectionCapabilities; 2386 mConnectionCapabilities = connectionCapabilities; 2387 for (Listener l : mListeners) { 2388 l.onConnectionCapabilitiesChanged(this); 2389 } 2390 2391 int strippedCaps = getConnectionCapabilities(); 2392 int xorCaps = previousCapabilities ^ strippedCaps; 2393 Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE, 2394 "Current: [%s], Removed [%s], Added [%s]", 2395 Connection.capabilitiesToStringShort(strippedCaps), 2396 Connection.capabilitiesToStringShort(previousCapabilities & xorCaps), 2397 Connection.capabilitiesToStringShort(strippedCaps & xorCaps)); 2398 } 2399 } 2400 2401 /** 2402 * For some states of Telecom, we need to modify this connection's capabilities: 2403 * - A user should not be able to pull an external call during an emergency call, so 2404 * CAPABILITY_CAN_PULL_CALL should be removed until the emergency call ends. 2405 * @param capabilities The original capabilities. 2406 * @return The stripped capabilities. 2407 */ stripUnsupportedCapabilities(int capabilities)2408 private int stripUnsupportedCapabilities(int capabilities) { 2409 if (!mIsPullExternalCallSupported) { 2410 if ((capabilities |= Connection.CAPABILITY_CAN_PULL_CALL) > 0) { 2411 capabilities &= ~Connection.CAPABILITY_CAN_PULL_CALL; 2412 Log.i(this, "stripCapabilitiesBasedOnState: CAPABILITY_CAN_PULL_CALL removed."); 2413 } 2414 } 2415 return capabilities; 2416 } 2417 setConnectionProperties(int connectionProperties)2418 public void setConnectionProperties(int connectionProperties) { 2419 Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString( 2420 connectionProperties)); 2421 2422 // Ensure the ConnectionService can't change the state of the self-managed property. 2423 if (isSelfManaged()) { 2424 connectionProperties |= Connection.PROPERTY_SELF_MANAGED; 2425 } else { 2426 connectionProperties &= ~Connection.PROPERTY_SELF_MANAGED; 2427 } 2428 2429 int changedProperties = mConnectionProperties ^ connectionProperties; 2430 2431 if (changedProperties != 0) { 2432 int previousProperties = mConnectionProperties; 2433 mConnectionProperties = connectionProperties; 2434 boolean didRttChange = 2435 (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT; 2436 if (didRttChange) { 2437 if ((mConnectionProperties & Connection.PROPERTY_IS_RTT) == 2438 Connection.PROPERTY_IS_RTT) { 2439 // If we already had RTT streams up, that means that either the call started 2440 // with RTT or the user previously requested to start RTT. Either way, don't 2441 // play the alert tone. 2442 if (!areRttStreamsInitialized()) { 2443 mCallsManager.playRttUpgradeToneForCall(this); 2444 } 2445 2446 createRttStreams(); 2447 // Call startRtt to pass the RTT pipes down to the connection service. 2448 // They already turned on the RTT property so no request should be sent. 2449 if (mConnectionService != null) { 2450 mConnectionService.startRtt(this, 2451 getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs()); 2452 } 2453 mWasEverRtt = true; 2454 if (isEmergencyCall()) { 2455 mCallsManager.mute(false); 2456 } 2457 } else { 2458 closeRttStreams(); 2459 mInCallToConnectionServiceStreams = null; 2460 mConnectionServiceToInCallStreams = null; 2461 } 2462 } 2463 mWasHighDefAudio = (connectionProperties & Connection.PROPERTY_HIGH_DEF_AUDIO) == 2464 Connection.PROPERTY_HIGH_DEF_AUDIO; 2465 mWasWifi = (connectionProperties & Connection.PROPERTY_WIFI) > 0; 2466 for (Listener l : mListeners) { 2467 l.onConnectionPropertiesChanged(this, didRttChange); 2468 } 2469 2470 boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL) 2471 == Connection.PROPERTY_IS_EXTERNAL_CALL; 2472 boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL) 2473 == Connection.PROPERTY_IS_EXTERNAL_CALL; 2474 if (wasExternal != isExternal) { 2475 Log.v(this, "setConnectionProperties: external call changed isExternal = %b", 2476 isExternal); 2477 Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal); 2478 if (isExternal) { 2479 // If there is an ongoing emergency call, remove the ability for this call to 2480 // be pulled. 2481 boolean isInEmergencyCall = mCallsManager.isInEmergencyCall(); 2482 setIsPullExternalCallSupported(!isInEmergencyCall); 2483 } 2484 for (Listener l : mListeners) { 2485 l.onExternalCallChanged(this, isExternal); 2486 } 2487 } 2488 2489 boolean wasDowngradedConference = 2490 (previousProperties & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0; 2491 boolean isDowngradedConference = 2492 (connectionProperties & Connection.PROPERTY_IS_DOWNGRADED_CONFERENCE) != 0; 2493 if (wasDowngradedConference && !isDowngradedConference) { 2494 Log.i(this, "DOWNGRADED_CONFERENCE property removed; setting" 2495 + " conference state to false"); 2496 setConferenceState(false); 2497 } 2498 2499 mAnalytics.addCallProperties(mConnectionProperties); 2500 2501 int xorProps = previousProperties ^ mConnectionProperties; 2502 Log.addEvent(this, LogUtils.Events.PROPERTY_CHANGE, 2503 "Current: [%s], Removed [%s], Added [%s]", 2504 Connection.propertiesToStringShort(mConnectionProperties), 2505 Connection.propertiesToStringShort(previousProperties & xorProps), 2506 Connection.propertiesToStringShort(mConnectionProperties & xorProps)); 2507 } 2508 } 2509 getSupportedAudioRoutes()2510 public int getSupportedAudioRoutes() { 2511 return mSupportedAudioRoutes; 2512 } 2513 setSupportedAudioRoutes(int audioRoutes)2514 void setSupportedAudioRoutes(int audioRoutes) { 2515 if (mSupportedAudioRoutes != audioRoutes) { 2516 mSupportedAudioRoutes = audioRoutes; 2517 } 2518 } 2519 2520 @VisibleForTesting getParentCall()2521 public Call getParentCall() { 2522 return mParentCall; 2523 } 2524 2525 @VisibleForTesting getChildCalls()2526 public List<Call> getChildCalls() { 2527 return mChildCalls; 2528 } 2529 2530 @VisibleForTesting wasConferencePreviouslyMerged()2531 public boolean wasConferencePreviouslyMerged() { 2532 return mWasConferencePreviouslyMerged; 2533 } 2534 isDisconnectingChildCall()2535 public boolean isDisconnectingChildCall() { 2536 return mIsDisconnectingChildCall; 2537 } 2538 2539 /** 2540 * Sets whether this call is a child call. 2541 */ maybeSetCallAsDisconnectingChild()2542 private void maybeSetCallAsDisconnectingChild() { 2543 if (mParentCall != null) { 2544 mIsDisconnectingChildCall = true; 2545 } 2546 } 2547 2548 @VisibleForTesting getConferenceLevelActiveCall()2549 public Call getConferenceLevelActiveCall() { 2550 return mConferenceLevelActiveCall; 2551 } 2552 getConnectionService()2553 public ConnectionServiceWrapper getConnectionService() { 2554 return mConnectionService; 2555 } 2556 2557 /** 2558 * Retrieves the {@link Context} for the call. 2559 * 2560 * @return The {@link Context}. 2561 */ getContext()2562 public Context getContext() { 2563 return mContext; 2564 } 2565 2566 @VisibleForTesting setConnectionService(ConnectionServiceWrapper service)2567 public void setConnectionService(ConnectionServiceWrapper service) { 2568 Log.i(this, "setConnectionService: service=[%s]", service); 2569 setConnectionService(service, null); 2570 } 2571 2572 @VisibleForTesting setConnectionService( ConnectionServiceWrapper service, ConnectionServiceWrapper remoteService )2573 public void setConnectionService( 2574 ConnectionServiceWrapper service, 2575 ConnectionServiceWrapper remoteService 2576 ) { 2577 Preconditions.checkNotNull(service); 2578 2579 clearConnectionService(); 2580 2581 service.incrementAssociatedCallCount(); 2582 2583 if (mFlags.updatedRcsCallCountTracking() && remoteService != null) { 2584 remoteService.incrementAssociatedCallCount(); 2585 mRemoteConnectionService = remoteService; 2586 } 2587 2588 mConnectionService = service; 2589 mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString()); 2590 mConnectionService.addCall(this); 2591 processCachedCallbacks(service); 2592 } 2593 2594 /** 2595 * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call. 2596 * Removes the call from its former {@link ConnectionServiceWrapper}, while still ensuring the 2597 * former {@link ConnectionServiceWrapper} is tracked as the mRemoteConnectionService for this 2598 * call so that the associatedCallCount of that {@link ConnectionServiceWrapper} is accurately 2599 * tracked until it is supposed to be unbound. 2600 * This method is used by the {@link ConnectionServiceWrapper} when handling {@link Connection} 2601 * and {@link Conference} additions via a ConnectionManager. 2602 * The original {@link android.telecom.ConnectionService} will directly add external calls and 2603 * conferences to Telecom as well as the ConnectionManager, which will add to Telecom. In these 2604 * cases since its first added to via the original CS, we want to change the CS responsible for 2605 * the call to the ConnectionManager rather than adding it again as another call/conference. 2606 * 2607 * @param service The new {@link ConnectionServiceWrapper}. 2608 */ replaceConnectionService(ConnectionServiceWrapper service)2609 public void replaceConnectionService(ConnectionServiceWrapper service) { 2610 Preconditions.checkNotNull(service); 2611 2612 if (mConnectionService != null) { 2613 ConnectionServiceWrapper serviceTemp = mConnectionService; 2614 2615 if (mFlags.updatedRcsCallCountTracking()) { 2616 // Continue to track the former CS for this call so that it doesn't unbind early: 2617 mRemoteConnectionService = serviceTemp; 2618 } 2619 2620 mConnectionService = null; 2621 serviceTemp.removeCall(this); 2622 2623 if (!mFlags.updatedRcsCallCountTracking()) { 2624 serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/); 2625 } 2626 } 2627 2628 service.incrementAssociatedCallCount(); 2629 mConnectionService = service; 2630 mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString()); 2631 } 2632 2633 /** 2634 * Clears the associated connection service. 2635 */ clearConnectionService()2636 void clearConnectionService() { 2637 if (mConnectionService != null) { 2638 ConnectionServiceWrapper serviceTemp = mConnectionService; 2639 ConnectionServiceWrapper remoteServiceTemp = mRemoteConnectionService; 2640 mRemoteConnectionService = null; 2641 mConnectionService = null; 2642 serviceTemp.removeCall(this); 2643 2644 // Decrementing the count can cause the service to unbind, which itself can trigger the 2645 // service-death code. Since the service death code tries to clean up any associated 2646 // calls, we need to make sure to remove that information (e.g., removeCall()) before 2647 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is 2648 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good 2649 // to do. 2650 decrementAssociatedCallCount(serviceTemp); 2651 2652 if (mFlags.updatedRcsCallCountTracking() && remoteServiceTemp != null) { 2653 decrementAssociatedCallCount(remoteServiceTemp); 2654 } 2655 } 2656 } 2657 2658 /** 2659 * Starts the create connection sequence. Upon completion, there should exist an active 2660 * connection through a connection service (or the call will have failed). 2661 * 2662 * @param phoneAccountRegistrar The phone account registrar. 2663 */ startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar)2664 void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { 2665 if (mCreateConnectionProcessor != null) { 2666 Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" + 2667 " due to a race between NewOutgoingCallIntentBroadcaster and " + 2668 "phoneAccountSelected, but is harmlessly resolved by ignoring the second " + 2669 "invocation."); 2670 return; 2671 } 2672 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, 2673 phoneAccountRegistrar, mCallsManager, mContext, mFlags, new Timeouts.Adapter()); 2674 mCreateConnectionProcessor.process(); 2675 } 2676 2677 @Override handleCreateConferenceSuccess( CallIdMapper idMapper, ParcelableConference conference)2678 public void handleCreateConferenceSuccess( 2679 CallIdMapper idMapper, 2680 ParcelableConference conference) { 2681 Log.v(this, "handleCreateConferenceSuccessful %s", conference); 2682 mIsCreateConnectionComplete = true; 2683 setTargetPhoneAccount(conference.getPhoneAccount()); 2684 setHandle(conference.getHandle(), conference.getHandlePresentation()); 2685 2686 setConnectionCapabilities(conference.getConnectionCapabilities()); 2687 setConnectionProperties(conference.getConnectionProperties()); 2688 setVideoProvider(conference.getVideoProvider()); 2689 setVideoState(conference.getVideoState()); 2690 setRingbackRequested(conference.isRingbackRequested()); 2691 setStatusHints(conference.getStatusHints()); 2692 putConnectionServiceExtras(conference.getExtras()); 2693 2694 switch (mCallDirection) { 2695 case CALL_DIRECTION_INCOMING: 2696 // Listeners (just CallsManager for now) will be responsible for checking whether 2697 // the call should be blocked. 2698 for (Listener l : mListeners) { 2699 l.onSuccessfulIncomingCall(this); 2700 } 2701 break; 2702 case CALL_DIRECTION_OUTGOING: 2703 for (Listener l : mListeners) { 2704 l.onSuccessfulOutgoingCall(this, 2705 getStateFromConnectionState(conference.getState())); 2706 } 2707 break; 2708 } 2709 } 2710 2711 @Override handleCreateConnectionSuccess( CallIdMapper idMapper, ParcelableConnection connection)2712 public void handleCreateConnectionSuccess( 2713 CallIdMapper idMapper, 2714 ParcelableConnection connection) { 2715 Log.v(this, "handleCreateConnectionSuccessful %s", connection); 2716 mIsCreateConnectionComplete = true; 2717 setTargetPhoneAccount(connection.getPhoneAccount()); 2718 setHandle(connection.getHandle(), connection.getHandlePresentation()); 2719 2720 setCallerDisplayName( 2721 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation()); 2722 setConnectionCapabilities(connection.getConnectionCapabilities()); 2723 setConnectionProperties(connection.getConnectionProperties()); 2724 setIsVoipAudioMode(connection.getIsVoipAudioMode()); 2725 setSupportedAudioRoutes(connection.getSupportedAudioRoutes()); 2726 setVideoProvider(connection.getVideoProvider()); 2727 setVideoState(connection.getVideoState()); 2728 setRingbackRequested(connection.isRingbackRequested()); 2729 setStatusHints(connection.getStatusHints()); 2730 putConnectionServiceExtras(connection.getExtras()); 2731 2732 mConferenceableCalls.clear(); 2733 for (String id : connection.getConferenceableConnectionIds()) { 2734 mConferenceableCalls.add(idMapper.getCall(id)); 2735 } 2736 2737 switch (mCallDirection) { 2738 case CALL_DIRECTION_INCOMING: 2739 setCallerNumberVerificationStatus(connection.getCallerNumberVerificationStatus()); 2740 2741 // Listeners (just CallsManager for now) will be responsible for checking whether 2742 // the call should be blocked. 2743 for (Listener l : mListeners) { 2744 l.onSuccessfulIncomingCall(this); 2745 } 2746 break; 2747 case CALL_DIRECTION_OUTGOING: 2748 for (Listener l : mListeners) { 2749 l.onSuccessfulOutgoingCall(this, 2750 getStateFromConnectionState(connection.getState())); 2751 } 2752 break; 2753 case CALL_DIRECTION_UNKNOWN: 2754 for (Listener l : mListeners) { 2755 l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection 2756 .getState())); 2757 } 2758 break; 2759 } 2760 } 2761 2762 @Override handleCreateConferenceFailure(DisconnectCause disconnectCause)2763 public void handleCreateConferenceFailure(DisconnectCause disconnectCause) { 2764 Log.i(this, "handleCreateConferenceFailure; callid=%s, disconnectCause=%s", 2765 getId(), disconnectCause); 2766 clearConnectionService(); 2767 setDisconnectCause(disconnectCause); 2768 mCallsManager.markCallAsDisconnected(this, disconnectCause); 2769 2770 switch (mCallDirection) { 2771 case CALL_DIRECTION_INCOMING: 2772 for (Listener listener : mListeners) { 2773 listener.onFailedIncomingCall(this); 2774 } 2775 break; 2776 case CALL_DIRECTION_OUTGOING: 2777 for (Listener listener : mListeners) { 2778 listener.onFailedOutgoingCall(this, disconnectCause); 2779 } 2780 break; 2781 } 2782 } 2783 2784 @Override handleCreateConnectionFailure(DisconnectCause disconnectCause)2785 public void handleCreateConnectionFailure(DisconnectCause disconnectCause) { 2786 Log.i(this, "handleCreateConnectionFailure; callid=%s, disconnectCause=%s", 2787 getId(), disconnectCause); 2788 clearConnectionService(); 2789 setDisconnectCause(disconnectCause); 2790 mCallsManager.markCallAsDisconnected(this, disconnectCause); 2791 2792 switch (mCallDirection) { 2793 case CALL_DIRECTION_INCOMING: 2794 for (Listener listener : mListeners) { 2795 listener.onFailedIncomingCall(this); 2796 } 2797 break; 2798 case CALL_DIRECTION_OUTGOING: 2799 for (Listener listener : mListeners) { 2800 listener.onFailedOutgoingCall(this, disconnectCause); 2801 } 2802 break; 2803 case CALL_DIRECTION_UNKNOWN: 2804 for (Listener listener : mListeners) { 2805 listener.onFailedUnknownCall(this); 2806 } 2807 break; 2808 } 2809 } 2810 2811 /** 2812 * Plays the specified DTMF tone. 2813 */ 2814 @VisibleForTesting playDtmfTone(char digit)2815 public void playDtmfTone(char digit) { 2816 if (mConnectionService == null) { 2817 Log.w(this, "playDtmfTone() request on a call without a connection service."); 2818 } else { 2819 Log.i(this, "Send playDtmfTone to connection service for call %s", this); 2820 mConnectionService.playDtmfTone(this, digit); 2821 Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit)); 2822 } 2823 mPlayingDtmfTone = digit; 2824 } 2825 2826 /** 2827 * Stops playing any currently playing DTMF tone. 2828 */ 2829 @VisibleForTesting stopDtmfTone()2830 public void stopDtmfTone() { 2831 if (mConnectionService == null) { 2832 Log.w(this, "stopDtmfTone() request on a call without a connection service."); 2833 } else { 2834 Log.i(this, "Send stopDtmfTone to connection service for call %s", this); 2835 Log.addEvent(this, LogUtils.Events.STOP_DTMF); 2836 mConnectionService.stopDtmfTone(this); 2837 } 2838 mPlayingDtmfTone = NO_DTMF_TONE; 2839 } 2840 2841 /** 2842 * @return {@code true} if a DTMF tone has been started via {@link #playDtmfTone(char)} but has 2843 * not been stopped via {@link #stopDtmfTone()}, {@code false} otherwise. 2844 */ isDtmfTonePlaying()2845 boolean isDtmfTonePlaying() { 2846 return mPlayingDtmfTone != NO_DTMF_TONE; 2847 } 2848 2849 /** 2850 * Silences the ringer. 2851 */ silence()2852 void silence() { 2853 if (mConnectionService == null) { 2854 Log.w(this, "silence() request on a call without a connection service."); 2855 } else { 2856 Log.i(this, "Send silence to connection service for call %s", this); 2857 Log.addEvent(this, LogUtils.Events.SILENCE); 2858 mConnectionService.silence(this); 2859 } 2860 } 2861 2862 @VisibleForTesting disconnect()2863 public CompletableFuture<Boolean> disconnect() { 2864 return disconnect(0); 2865 } 2866 disconnect(String reason)2867 public CompletableFuture<Boolean> disconnect(String reason) { 2868 return disconnect(0, reason); 2869 } 2870 2871 /** 2872 * Attempts to disconnect the call through the connection service. 2873 */ 2874 @VisibleForTesting disconnect(long disconnectionTimeout)2875 public CompletableFuture<Boolean> disconnect(long disconnectionTimeout) { 2876 return disconnect(disconnectionTimeout, "internal" /* reason */); 2877 } 2878 2879 /** 2880 * Attempts to disconnect the call through the connection service. 2881 * @param reason the reason for the disconnect; used for logging purposes only. In some cases 2882 * this can be a package name if the disconnect was initiated through an API such 2883 * as TelecomManager. 2884 */ 2885 @VisibleForTesting disconnect(long disconnectionTimeout, String reason)2886 public CompletableFuture<Boolean> disconnect(long disconnectionTimeout, 2887 String reason) { 2888 Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT, reason); 2889 2890 // Track that the call is now locally disconnecting. 2891 setLocallyDisconnecting(true); 2892 maybeSetCallAsDisconnectingChild(); 2893 2894 CompletableFuture<Boolean> disconnectFutureHandler = 2895 CompletableFuture.completedFuture(false); 2896 if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT || 2897 mState == CallState.CONNECTING) { 2898 Log.i(this, "disconnect: Aborting call %s", getId()); 2899 if (mFlags.enableCallSequencing()) { 2900 disconnectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall( 2901 false /* shouldDisconnectUponTimeout */, "disconnect", 2902 CallState.DISCONNECTED, CallState.ABORTED); 2903 } 2904 abort(disconnectionTimeout); 2905 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) { 2906 if (mState == CallState.AUDIO_PROCESSING && !hasGoneActiveBefore()) { 2907 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED)); 2908 } else if (mState == CallState.SIMULATED_RINGING) { 2909 // This is the case where the dialer calls disconnect() because the call timed out 2910 // or an emergency call was dialed while in this state. 2911 // Override the disconnect cause to MISSED 2912 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.MISSED)); 2913 } 2914 if (mTransactionalService != null) { 2915 disconnectFutureHandler = mTransactionalService.onDisconnect(this, 2916 getDisconnectCause()); 2917 Log.i(this, "Send Disconnect to transactional service for call"); 2918 } else if (mConnectionService == null) { 2919 Log.e(this, new Exception(), "disconnect() request on a call without a" 2920 + " connection service."); 2921 } else { 2922 Log.i(this, "Send disconnect to connection service for call: %s", this); 2923 // The call isn't officially disconnected until the connection service 2924 // confirms that the call was actually disconnected. Only then is the 2925 // association between call and connection service severed, see 2926 // {@link CallsManager#markCallAsDisconnected}. 2927 if (mFlags.enableCallSequencing()) { 2928 disconnectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall( 2929 false /* shouldDisconnectUponTimeout */, "disconnect", 2930 CallState.DISCONNECTED); 2931 } 2932 mConnectionService.disconnect(this); 2933 } 2934 } 2935 return disconnectFutureHandler; 2936 } 2937 abort(long disconnectionTimeout)2938 void abort(long disconnectionTimeout) { 2939 if (mCreateConnectionProcessor != null && 2940 !mCreateConnectionProcessor.isProcessingComplete()) { 2941 mCreateConnectionProcessor.abort(); 2942 } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT 2943 || mState == CallState.CONNECTING) { 2944 if (disconnectionTimeout > 0) { 2945 // If the cancelation was from NEW_OUTGOING_CALL with a timeout of > 0 2946 // milliseconds, do not destroy the call. 2947 // Instead, we announce the cancellation and CallsManager handles 2948 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and 2949 // then re-dial them quickly using a gateway, allowing the first call to end 2950 // causes jank. This timeout allows CallsManager to transition the first call into 2951 // the second call so that in-call only ever sees a single call...eliminating the 2952 // jank altogether. The app will also be able to set the timeout via an extra on 2953 // the ordered broadcast. 2954 for (Listener listener : mListeners) { 2955 if (listener.onCanceledViaNewOutgoingCallBroadcast( 2956 this, disconnectionTimeout)) { 2957 // The first listener to handle this wins. A return value of true means that 2958 // the listener will handle the disconnection process later and so we 2959 // should not continue it here. 2960 setLocallyDisconnecting(false); 2961 return; 2962 } 2963 } 2964 } 2965 2966 handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED)); 2967 } else { 2968 Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING"); 2969 } 2970 } 2971 2972 /** 2973 * Answers the call if it is ringing. 2974 * 2975 * @param videoState The video state in which to answer the call. 2976 */ 2977 @VisibleForTesting answer(int videoState)2978 public CompletableFuture<Boolean> answer(int videoState) { 2979 CompletableFuture<Boolean> answerCallFuture = CompletableFuture.completedFuture(false); 2980 // Check to verify that the call is still in the ringing state. A call can change states 2981 // between the time the user hits 'answer' and Telecom receives the command. 2982 if (isRinging("answer")) { 2983 Log.addEvent(this, LogUtils.Events.REQUEST_ACCEPT); 2984 if (!isVideoCallingSupportedByPhoneAccount() && VideoProfile.isVideo(videoState)) { 2985 // Video calling is not supported, yet the InCallService is attempting to answer as 2986 // video. We will simply answer as audio-only. 2987 videoState = VideoProfile.STATE_AUDIO_ONLY; 2988 } 2989 // At this point, we are asking the connection service to answer but we don't assume 2990 // that it will work. Instead, we wait until confirmation from the connection service 2991 // that the call is in a non-STATE_RINGING state before changing the UI. See 2992 // {@link ConnectionServiceAdapter#setActive} and other set* methods. 2993 if (mConnectionService != null) { 2994 if (mFlags.enableCallSequencing()) { 2995 answerCallFuture = awaitCallStateChangeAndMaybeDisconnectCall( 2996 false /* shouldDisconnectUponTimeout */, "answer", CallState.ACTIVE); 2997 } 2998 mConnectionService.answer(this, videoState); 2999 } else if (mTransactionalService != null) { 3000 return mTransactionalService.onAnswer(this, videoState); 3001 } else { 3002 Log.e(this, new NullPointerException(), 3003 "answer call failed due to null CS callId=%s", getId()); 3004 } 3005 } 3006 return answerCallFuture; 3007 } 3008 3009 /** 3010 * Answers the call on the connectionservice side in order to start audio processing. 3011 * 3012 * This pathway keeps the call in the ANSWERED state until the connection service confirms the 3013 * answer, at which point we'll set it to AUDIO_PROCESSING. However, to prevent any other 3014 * components from seeing the churn between RINGING -> ANSWERED -> AUDIO_PROCESSING, we'll 3015 * refrain from tracking this call in CallsManager until we've stabilized in AUDIO_PROCESSING 3016 */ answerForAudioProcessing()3017 public void answerForAudioProcessing() { 3018 if (mState != CallState.RINGING) { 3019 Log.w(this, "Trying to audio-process a non-ringing call: id=%s", mId); 3020 return; 3021 } 3022 3023 if (mConnectionService != null) { 3024 mConnectionService.answer(this, VideoProfile.STATE_AUDIO_ONLY); 3025 } else { 3026 Log.e(this, new NullPointerException(), 3027 "answer call (audio processing) failed due to null CS callId=%s", getId()); 3028 } 3029 3030 Log.addEvent(this, LogUtils.Events.REQUEST_PICKUP_FOR_AUDIO_PROCESSING); 3031 } 3032 setAudioProcessingRequestingApp(CharSequence appName)3033 public void setAudioProcessingRequestingApp(CharSequence appName) { 3034 mAudioProcessingRequestingApp = appName; 3035 } 3036 getAudioProcessingRequestingApp()3037 public CharSequence getAudioProcessingRequestingApp() { 3038 return mAudioProcessingRequestingApp; 3039 } 3040 3041 /** 3042 * Deflects the call if it is ringing. 3043 * 3044 * @param address address to be deflected to. 3045 */ 3046 @VisibleForTesting deflect(Uri address)3047 public void deflect(Uri address) { 3048 // Check to verify that the call is still in the ringing state. A call can change states 3049 // between the time the user hits 'deflect' and Telecomm receives the command. 3050 if (isRinging("deflect")) { 3051 // At this point, we are asking the connection service to deflect but we don't assume 3052 // that it will work. Instead, we wait until confirmation from the connection service 3053 // that the call is in a non-STATE_RINGING state before changing the UI. See 3054 // {@link ConnectionServiceAdapter#setActive} and other set* methods. 3055 mVideoStateHistory |= mVideoState; 3056 if (mConnectionService != null) { 3057 mConnectionService.deflect(this, address); 3058 } else { 3059 Log.e(this, new NullPointerException(), 3060 "deflect call failed due to null CS callId=%s", getId()); 3061 } 3062 Log.addEvent(this, LogUtils.Events.REQUEST_DEFLECT, Log.pii(address)); 3063 } 3064 } 3065 3066 /** 3067 * Rejects the call if it is ringing. 3068 * 3069 * @param rejectWithMessage Whether to send a text message as part of the call rejection. 3070 * @param textMessage An optional text message to send as part of the rejection. 3071 */ 3072 @VisibleForTesting reject(boolean rejectWithMessage, String textMessage)3073 public void reject(boolean rejectWithMessage, String textMessage) { 3074 reject(rejectWithMessage, textMessage, "internal" /** reason */); 3075 } 3076 3077 /** 3078 * Rejects the call if it is ringing. 3079 * 3080 * @param rejectWithMessage Whether to send a text message as part of the call rejection. 3081 * @param textMessage An optional text message to send as part of the rejection. 3082 * @param reason The reason for the reject; used for logging purposes. May be a package name 3083 * if the reject is initiated from an API such as TelecomManager. 3084 */ 3085 @VisibleForTesting reject(boolean rejectWithMessage, String textMessage, String reason)3086 public CompletableFuture<Boolean> reject(boolean rejectWithMessage, 3087 String textMessage, String reason) { 3088 CompletableFuture<Boolean> rejectFutureHandler = CompletableFuture.completedFuture(false); 3089 if (mState == CallState.SIMULATED_RINGING) { 3090 Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason); 3091 // This handles the case where the user manually rejects a call that's in simulated 3092 // ringing. Since the call is already active on the connectionservice side, we want to 3093 // hangup, not reject. 3094 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED)); 3095 if (mTransactionalService != null) { 3096 return mTransactionalService.onDisconnect(this, 3097 new DisconnectCause(DisconnectCause.REJECTED)); 3098 } else if (mConnectionService != null) { 3099 if (mFlags.enableCallSequencing()) { 3100 rejectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall( 3101 false /* shouldDisconnectUponTimeout */, "reject", 3102 CallState.DISCONNECTED); 3103 } 3104 mConnectionService.disconnect(this); 3105 return rejectFutureHandler; 3106 } else { 3107 Log.e(this, new NullPointerException(), 3108 "reject call failed due to null CS callId=%s", getId()); 3109 } 3110 } else if (isRinging("reject") || isAnswered("reject")) { 3111 Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason); 3112 // Ensure video state history tracks video state at time of rejection. 3113 mVideoStateHistory |= mVideoState; 3114 3115 if (mTransactionalService != null) { 3116 return mTransactionalService.onDisconnect(this, 3117 new DisconnectCause(DisconnectCause.REJECTED)); 3118 } else if (mConnectionService != null) { 3119 if (mFlags.enableCallSequencing()) { 3120 rejectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall( 3121 false /* shouldDisconnectUponTimeout */, "reject", 3122 CallState.DISCONNECTED); 3123 } 3124 mConnectionService.reject(this, rejectWithMessage, textMessage); 3125 return rejectFutureHandler; 3126 } else { 3127 Log.e(this, new NullPointerException(), 3128 "reject call failed due to null CS callId=%s", getId()); 3129 } 3130 } 3131 return rejectFutureHandler; 3132 } 3133 3134 /** 3135 * Reject this Telecom call with the user-indicated reason. 3136 * @param rejectReason The user-indicated reason fore rejecting the call. 3137 */ reject(@ndroid.telecom.Call.RejectReason int rejectReason)3138 public CompletableFuture<Boolean> reject(@android.telecom.Call.RejectReason int rejectReason) { 3139 CompletableFuture<Boolean> rejectFutureHandler = CompletableFuture.completedFuture(false); 3140 if (mState == CallState.SIMULATED_RINGING) { 3141 Log.addEvent(this, LogUtils.Events.REQUEST_REJECT); 3142 // This handles the case where the user manually rejects a call that's in simulated 3143 // ringing. Since the call is already active on the connectionservice side, we want to 3144 // hangup, not reject. 3145 // Since its simulated reason we can't pass along the reject reason. 3146 setOverrideDisconnectCauseCode(new DisconnectCause(DisconnectCause.REJECTED)); 3147 if (mTransactionalService != null) { 3148 return mTransactionalService.onDisconnect(this, 3149 new DisconnectCause(DisconnectCause.REJECTED)); 3150 } else if (mConnectionService != null) { 3151 if (mFlags.enableCallSequencing()) { 3152 rejectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall( 3153 false /* shouldDisconnectUponTimeout */, "reject", 3154 CallState.DISCONNECTED); 3155 } 3156 mConnectionService.disconnect(this); 3157 } else { 3158 Log.e(this, new NullPointerException(), 3159 "reject call failed due to null CS callId=%s", getId()); 3160 } 3161 } else if (isRinging("reject") || isAnswered("reject")) { 3162 Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, rejectReason); 3163 // Ensure video state history tracks video state at time of rejection. 3164 mVideoStateHistory |= mVideoState; 3165 if (mTransactionalService != null) { 3166 return mTransactionalService.onDisconnect(this, 3167 new DisconnectCause(DisconnectCause.REJECTED)); 3168 } else if (mConnectionService != null) { 3169 if (mFlags.enableCallSequencing()) { 3170 rejectFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall( 3171 false /* shouldDisconnectUponTimeout */, "reject", 3172 CallState.DISCONNECTED); 3173 } 3174 mConnectionService.rejectWithReason(this, rejectReason); 3175 } else { 3176 Log.e(this, new NullPointerException(), 3177 "reject call failed due to null CS callId=%s", getId()); 3178 } 3179 } 3180 return rejectFutureHandler; 3181 } 3182 3183 /** 3184 * Transfers the call if it is active or held. 3185 * 3186 * @param number number to be transferred to. 3187 * @param isConfirmationRequired whether for blind or assured transfer. 3188 */ 3189 @VisibleForTesting transfer(Uri number, boolean isConfirmationRequired)3190 public void transfer(Uri number, boolean isConfirmationRequired) { 3191 if (mState == CallState.ACTIVE || mState == CallState.ON_HOLD) { 3192 if (mTransactionalService != null) { 3193 Log.i(this, "transfer: called on TransactionalService. doing nothing"); 3194 } else if (mConnectionService != null) { 3195 mConnectionService.transfer(this, number, isConfirmationRequired); 3196 } else { 3197 Log.e(this, new NullPointerException(), 3198 "transfer call failed due to null CS callId=%s", getId()); 3199 } 3200 Log.addEvent(this, LogUtils.Events.REQUEST_TRANSFER, Log.pii(number)); 3201 } 3202 } 3203 3204 /** 3205 * Transfers the call when this call is active and the other call is held. 3206 * This is for Consultative call transfer. 3207 * 3208 * @param otherCall The other {@link Call} to which this call will be transferred. 3209 */ 3210 @VisibleForTesting transfer(Call otherCall)3211 public void transfer(Call otherCall) { 3212 if (mState == CallState.ACTIVE && 3213 (otherCall != null && otherCall.getState() == CallState.ON_HOLD)) { 3214 if (mTransactionalService != null) { 3215 Log.i(this, "transfer: called on TransactionalService. doing nothing"); 3216 } else if (mConnectionService != null) { 3217 mConnectionService.transfer(this, otherCall); 3218 } else { 3219 Log.e(this, new NullPointerException(), 3220 "transfer call failed due to null CS callId=%s", getId()); 3221 } 3222 Log.addEvent(this, LogUtils.Events.REQUEST_CONSULTATIVE_TRANSFER, otherCall); 3223 } 3224 } 3225 3226 /** 3227 * Puts the call on hold if it is currently active. 3228 */ 3229 @VisibleForTesting hold()3230 public CompletableFuture<Boolean> hold() { 3231 return hold(null /* reason */); 3232 } 3233 3234 /** 3235 * This method requests the ConnectionService or TransactionalService hosting the call to put 3236 * the call on hold 3237 */ hold(String reason)3238 public CompletableFuture<Boolean> hold(String reason) { 3239 CompletableFuture<Boolean> holdFutureHandler = CompletableFuture.completedFuture(false); 3240 if (mState == CallState.ACTIVE) { 3241 Log.addEvent(this, LogUtils.Events.REQUEST_HOLD, reason); 3242 if (mTransactionalService != null) { 3243 return mTransactionalService.onSetInactive(this); 3244 } else if (mConnectionService != null) { 3245 if (mFlags.transactionalCsVerifier() || mFlags.enableCallSequencing()) { 3246 holdFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall(isSelfManaged(), 3247 "hold", CallState.ON_HOLD, CallState.DISCONNECTED).thenCompose( 3248 (result) -> { 3249 // Explicitly handle self-managed hold failures where we 3250 // explicitly disconnect the call and treat it as a 3251 // completed transaction. 3252 if (!result && isSelfManaged()) { 3253 Log.i(this, "hold: Completing transaction " 3254 + "after disconnecting held call."); 3255 return CompletableFuture.completedFuture(true); 3256 } 3257 return CompletableFuture.completedFuture(result); 3258 });; 3259 } 3260 mConnectionService.hold(this); 3261 return holdFutureHandler; 3262 } else { 3263 Log.e(this, new NullPointerException(), 3264 "hold call failed due to null CS callId=%s", getId()); 3265 } 3266 } 3267 return holdFutureHandler; 3268 } 3269 3270 /** 3271 * helper that can be used for any callback that requests a call state change and wants to 3272 * verify the change 3273 */ awaitCallStateChangeAndMaybeDisconnectCall( boolean shouldDisconnectUponTimeout, String callingMethod, int... targetCallStates)3274 public CompletableFuture<Boolean> awaitCallStateChangeAndMaybeDisconnectCall( 3275 boolean shouldDisconnectUponTimeout, String callingMethod, int... targetCallStates) { 3276 TransactionManager tm = TransactionManager.getInstance(); 3277 CallTransaction callTransaction = new VerifyCallStateChangeTransaction( 3278 mCallsManager.getLock(), this, targetCallStates); 3279 return tm.addTransaction(callTransaction, 3280 new OutcomeReceiver<>() { 3281 @Override 3282 public void onResult(CallTransactionResult result) { 3283 Log.i(this, "awaitCallStateChangeAndMaybeDisconnectCall: %s: onResult:" 3284 + " due to CallException=[%s]", callingMethod, result); 3285 } 3286 3287 @Override 3288 public void onError(CallException e) { 3289 Log.i(this, "awaitCallStateChangeAndMaybeDisconnectCall: %s: onError" 3290 + " due to CallException=[%s]", callingMethod, e); 3291 if (shouldDisconnectUponTimeout) { 3292 mCallsManager.markCallAsDisconnected(Call.this, 3293 new DisconnectCause(DisconnectCause.ERROR, 3294 "did not hold in timeout window")); 3295 mCallsManager.markCallAsRemoved(Call.this); 3296 } 3297 } 3298 }); 3299 } 3300 3301 /** 3302 * Releases the call from hold if it is currently active. 3303 */ 3304 @VisibleForTesting 3305 public CompletableFuture<Boolean> unhold() { 3306 return unhold(null /* reason */); 3307 } 3308 3309 public CompletableFuture<Boolean> unhold(String reason) { 3310 CompletableFuture<Boolean> unholdFutureHandler = CompletableFuture.completedFuture(false); 3311 if (mState == CallState.ON_HOLD) { 3312 Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD, reason); 3313 if (mTransactionalService != null){ 3314 return mTransactionalService.onSetActive(this); 3315 } else if (mConnectionService != null){ 3316 if (mFlags.enableCallSequencing()) { 3317 unholdFutureHandler = awaitCallStateChangeAndMaybeDisconnectCall( 3318 false /* shouldDisconnectUponTimeout */, "unhold", CallState.ACTIVE); 3319 } 3320 mConnectionService.unhold(this); 3321 return unholdFutureHandler; 3322 } else { 3323 Log.e(this, new NullPointerException(), 3324 "unhold call failed due to null CS callId=%s", getId()); 3325 } 3326 } 3327 return unholdFutureHandler; 3328 } 3329 3330 /** Checks if this is a live call or not. */ 3331 @VisibleForTesting 3332 public boolean isAlive() { 3333 switch (mState) { 3334 case CallState.NEW: 3335 case CallState.RINGING: 3336 case CallState.ANSWERED: 3337 case CallState.DISCONNECTED: 3338 case CallState.ABORTED: 3339 return false; 3340 default: 3341 return true; 3342 } 3343 } 3344 3345 public boolean isActive() { 3346 return mState == CallState.ACTIVE; 3347 } 3348 3349 @VisibleForTesting 3350 public Bundle getExtras() { 3351 return mExtras; 3352 } 3353 3354 /** 3355 * Adds extras to the extras bundle associated with this {@link Call}, as made by a 3356 * {@link ConnectionService} or other non {@link android.telecom.InCallService} source. 3357 * 3358 * @param extras The extras. 3359 */ 3360 public void putConnectionServiceExtras(Bundle extras) { 3361 putExtras(SOURCE_CONNECTION_SERVICE, extras, null); 3362 } 3363 3364 /** 3365 * Adds extras to the extras bundle associated with this {@link Call}, as made by a 3366 * {@link android.telecom.InCallService}. 3367 * @param extras the extras. 3368 * @param requestingPackageName the package name of the {@link android.telecom.InCallService} 3369 * which requested the extras changed; required so that when we 3370 * have {@link InCallController} notify other 3371 * {@link android.telecom.InCallService}s we don't notify the 3372 * originator of their own change. 3373 */ 3374 public void putInCallServiceExtras(Bundle extras, String requestingPackageName) { 3375 putExtras(SOURCE_INCALL_SERVICE, extras, requestingPackageName); 3376 } 3377 3378 /** 3379 * Adds extras to the extras bundle associated with this {@link Call}. 3380 * 3381 * Note: this method needs to know the source of the extras change (see 3382 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which 3383 * originate from a connection service will only be notified to incall services. Changes 3384 * originating from the InCallServices will notify the connection service of the 3385 * change, as well as other InCallServices other than the originator. 3386 * 3387 * @param source The source of the extras addition. 3388 * @param extras The extras. 3389 * @param requestingPackageName The package name which requested the extras change. For 3390 * {@link #SOURCE_INCALL_SERVICE} will be populated with the 3391 * package name of the ICS that requested the change. 3392 */ 3393 private void putExtras(int source, Bundle extras, String requestingPackageName) { 3394 if (extras == null) { 3395 return; 3396 } 3397 if (mExtras == null) { 3398 mExtras = new Bundle(); 3399 } 3400 mExtras.putAll(extras); 3401 3402 for (Listener l : mListeners) { 3403 l.onExtrasChanged(this, source, extras, requestingPackageName); 3404 } 3405 3406 // If mExtra shows that the call using Volte, record it with mWasVolte 3407 if (mExtras.containsKey(TelecomManager.EXTRA_CALL_NETWORK_TYPE) && 3408 mExtras.get(TelecomManager.EXTRA_CALL_NETWORK_TYPE) 3409 .equals(TelephonyManager.NETWORK_TYPE_LTE)) { 3410 mWasVolte = true; 3411 } 3412 3413 if (extras.containsKey(Connection.EXTRA_ORIGINAL_CONNECTION_ID)) { 3414 setOriginalConnectionId(extras.getString(Connection.EXTRA_ORIGINAL_CONNECTION_ID)); 3415 } 3416 3417 if (extras.containsKey(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS) 3418 && source == SOURCE_CONNECTION_SERVICE) { 3419 int callerNumberVerificationStatus = 3420 extras.getInt(Connection.EXTRA_CALLER_NUMBER_VERIFICATION_STATUS); 3421 if (mCallerNumberVerificationStatus != callerNumberVerificationStatus) { 3422 Log.addEvent(this, LogUtils.Events.VERSTAT_CHANGED, callerNumberVerificationStatus); 3423 setCallerNumberVerificationStatus(callerNumberVerificationStatus); 3424 } 3425 } 3426 3427 if (extras.containsKey(Connection.EXTRA_ANSWERING_DROPS_FG_CALL)) { 3428 CharSequence appName = 3429 extras.getCharSequence(Connection.EXTRA_ANSWERING_DROPS_FG_CALL_APP_NAME); 3430 Log.addEvent(this, LogUtils.Events.ANSWER_DROPS_FG, 3431 "Answering will drop FG call from %s", appName); 3432 } 3433 3434 // The remote connection service API can track the phone account which was originally 3435 // requested to create a connection via the remote connection service API; we store that so 3436 // we have some visibility into how a call was actually placed. 3437 if (mExtras.containsKey(Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE)) { 3438 setRemotePhoneAccountHandle(extras.getParcelable( 3439 Connection.EXTRA_REMOTE_PHONE_ACCOUNT_HANDLE)); 3440 } 3441 3442 if (mExtras.containsKey(TelecomManager.EXTRA_DO_NOT_LOG_CALL)) { 3443 if (source != SOURCE_CONNECTION_SERVICE || !mIsModifyStatePermissionGranted) { 3444 mExtras.remove(TelecomManager.EXTRA_DO_NOT_LOG_CALL); 3445 } 3446 } 3447 3448 // If the change originated from an InCallService, notify the connection service. 3449 if (source == SOURCE_INCALL_SERVICE) { 3450 Log.addEvent(this, LogUtils.Events.ICS_EXTRAS_CHANGED); 3451 if (mTransactionalService != null) { 3452 Log.i(this, "putExtras: called on TransactionalService. doing nothing"); 3453 } else if (mConnectionService != null) { 3454 mConnectionService.onExtrasChanged(this, mExtras); 3455 } else { 3456 Log.w(this, "putExtras failed due to null CS callId=%s", getId()); 3457 } 3458 } 3459 } 3460 3461 private boolean isModifyPhoneStatePermissionGranted(PhoneAccountHandle phoneAccountHandle) { 3462 if (phoneAccountHandle == null) { 3463 return false; 3464 } 3465 String packageName = phoneAccountHandle.getComponentName().getPackageName(); 3466 return PackageManager.PERMISSION_GRANTED == mContext.getPackageManager().checkPermission( 3467 android.Manifest.permission.MODIFY_PHONE_STATE, packageName); 3468 } 3469 3470 /** 3471 * Removes extras from the extras bundle associated with this {@link Call}. 3472 * 3473 * Note: this method needs to know the source of the extras change (see 3474 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which 3475 * originate from a connection service will only be notified to incall services. Likewise, 3476 * changes originating from the incall services will only notify the connection service of the 3477 * change. 3478 * 3479 * @param source The source of the extras removal. 3480 * @param keys The extra keys to remove. 3481 */ 3482 void removeExtras(int source, List<String> keys) { 3483 if (mExtras == null) { 3484 return; 3485 } 3486 for (String key : keys) { 3487 mExtras.remove(key); 3488 } 3489 3490 for (Listener l : mListeners) { 3491 l.onExtrasRemoved(this, source, keys); 3492 } 3493 3494 // If the change originated from an InCallService, notify the connection service. 3495 if (source == SOURCE_INCALL_SERVICE) { 3496 if (mTransactionalService != null) { 3497 Log.i(this, "removeExtras: called on TransactionalService. doing nothing"); 3498 } else if (mConnectionService != null) { 3499 mConnectionService.onExtrasChanged(this, mExtras); 3500 } else { 3501 Log.e(this, new NullPointerException(), 3502 "removeExtras failed due to null CS callId=%s", getId()); 3503 } 3504 } 3505 } 3506 3507 @VisibleForTesting 3508 public Bundle getIntentExtras() { 3509 return mIntentExtras; 3510 } 3511 3512 void setIntentExtras(Bundle extras) { 3513 mIntentExtras = extras; 3514 } 3515 3516 public Intent getOriginalCallIntent() { 3517 return mOriginalCallIntent; 3518 } 3519 3520 public void setOriginalCallIntent(Intent intent) { 3521 mOriginalCallIntent = intent; 3522 } 3523 3524 /** 3525 * @return the uri of the contact associated with this call. 3526 */ 3527 @VisibleForTesting 3528 public Uri getContactUri() { 3529 if (mCallerInfo == null || !mCallerInfo.contactExists) { 3530 return getHandle(); 3531 } 3532 return Contacts.getLookupUri(mCallerInfo.getContactId(), mCallerInfo.lookupKey); 3533 } 3534 3535 Uri getRingtone() { 3536 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri; 3537 } 3538 3539 void onPostDialWait(String remaining) { 3540 for (Listener l : mListeners) { 3541 l.onPostDialWait(this, remaining); 3542 } 3543 } 3544 3545 void onPostDialChar(char nextChar) { 3546 for (Listener l : mListeners) { 3547 l.onPostDialChar(this, nextChar); 3548 } 3549 } 3550 3551 void postDialContinue(boolean proceed) { 3552 if (mTransactionalService != null) { 3553 Log.i(this, "postDialContinue: called on TransactionalService. doing nothing"); 3554 } else if (mConnectionService != null) { 3555 mConnectionService.onPostDialContinue(this, proceed); 3556 } else { 3557 Log.e(this, new NullPointerException(), 3558 "postDialContinue failed due to null CS callId=%s", getId()); 3559 } 3560 } 3561 3562 void conferenceWith(Call otherCall) { 3563 if (mTransactionalService != null) { 3564 Log.i(this, "conferenceWith: called on TransactionalService. doing nothing"); 3565 } else if (mConnectionService == null) { 3566 Log.w(this, "conference requested on a call without a connection service."); 3567 } else { 3568 Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH, otherCall); 3569 mConnectionService.conference(this, otherCall); 3570 } 3571 } 3572 3573 void splitFromConference() { 3574 if (mTransactionalService != null) { 3575 Log.i(this, "splitFromConference: called on TransactionalService. doing nothing"); 3576 } else if (mConnectionService == null) { 3577 Log.w(this, "splitting from conference call without a connection service"); 3578 } else { 3579 Log.addEvent(this, LogUtils.Events.SPLIT_FROM_CONFERENCE); 3580 mConnectionService.splitFromConference(this); 3581 } 3582 } 3583 3584 @VisibleForTesting 3585 public void mergeConference() { 3586 if (mTransactionalService != null) { 3587 Log.i(this, "mergeConference: called on TransactionalService. doing nothing"); 3588 } else if (mConnectionService == null) { 3589 Log.w(this, "merging conference calls without a connection service."); 3590 } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) { 3591 Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH); 3592 mConnectionService.mergeConference(this); 3593 mWasConferencePreviouslyMerged = true; 3594 } 3595 } 3596 3597 @VisibleForTesting 3598 public void swapConference() { 3599 if (mTransactionalService != null) { 3600 Log.i(this, "swapConference: called on TransactionalService. doing nothing"); 3601 } else if (mConnectionService == null) { 3602 Log.w(this, "swapping conference calls without a connection service."); 3603 } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) { 3604 Log.addEvent(this, LogUtils.Events.SWAP); 3605 mConnectionService.swapConference(this); 3606 switch (mChildCalls.size()) { 3607 case 1: 3608 mConferenceLevelActiveCall = mChildCalls.get(0); 3609 break; 3610 case 2: 3611 // swap 3612 mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ? 3613 mChildCalls.get(1) : mChildCalls.get(0); 3614 break; 3615 default: 3616 // For anything else 0, or 3+, set it to null since it is impossible to tell. 3617 mConferenceLevelActiveCall = null; 3618 break; 3619 } 3620 for (Listener l : mListeners) { 3621 l.onCdmaConferenceSwap(this); 3622 } 3623 } 3624 } 3625 3626 public void addConferenceParticipants(List<Uri> participants) { 3627 if (mTransactionalService != null) { 3628 Log.i(this, "addConferenceParticipants: called on TransactionalService. doing nothing"); 3629 } else if (mConnectionService == null) { 3630 Log.w(this, "adding conference participants without a connection service."); 3631 } else if (can(Connection.CAPABILITY_ADD_PARTICIPANT)) { 3632 Log.addEvent(this, LogUtils.Events.ADD_PARTICIPANT); 3633 mConnectionService.addConferenceParticipants(this, participants); 3634 } 3635 } 3636 3637 /** 3638 * Initiates a request to the connection service to pull this call. 3639 * <p> 3640 * This method can only be used for calls that have the 3641 * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and 3642 * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set. 3643 * <p> 3644 * An external call is a representation of a call which is taking place on another device 3645 * associated with a PhoneAccount on this device. Issuing a request to pull the external call 3646 * tells the {@link android.telecom.ConnectionService} that it should move the call from the 3647 * other device to this one. An example of this is the IMS multi-endpoint functionality. A 3648 * user may have two phones with the same phone number. If the user is engaged in an active 3649 * call on their first device, the network will inform the second device of that ongoing call in 3650 * the form of an external call. The user may wish to continue their conversation on the second 3651 * device, so will issue a request to pull the call to the second device. 3652 * <p> 3653 * Requests to pull a call which is not external, or a call which is not pullable are ignored. 3654 * If there is an ongoing emergency call, pull requests are also ignored. 3655 */ 3656 public void pullExternalCall() { 3657 if (mTransactionalService != null) { 3658 Log.i(this, "transfer: called on TransactionalService. doing nothing"); 3659 return; 3660 } 3661 3662 if (mConnectionService == null) { 3663 Log.w(this, "pulling a call without a connection service."); 3664 } 3665 3666 if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) { 3667 Log.w(this, "pullExternalCall - call %s is not an external call.", mId); 3668 return; 3669 } 3670 3671 if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) { 3672 Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId); 3673 return; 3674 } 3675 3676 if (mCallsManager.isInEmergencyCall()) { 3677 Log.w(this, "pullExternalCall = pullExternalCall - call %s is external but can not be" 3678 + " pulled while an emergency call is in progress.", mId); 3679 mToastFactory.makeText(mContext, R.string.toast_emergency_can_not_pull_call, 3680 Toast.LENGTH_LONG); 3681 return; 3682 } 3683 3684 Log.addEvent(this, LogUtils.Events.REQUEST_PULL); 3685 mConnectionService.pullExternalCall(this); 3686 } 3687 3688 /** 3689 * Sends a call event to the {@link ConnectionService} for this call. 3690 * 3691 * @param event The call event. 3692 * @param extras Associated extras. 3693 */ 3694 public void sendCallEvent(String event, Bundle extras) { 3695 if (mConnectionService != null || mTransactionalService != null) { 3696 // Relay bluetooth call quality reports to the call diagnostic service. 3697 if (BluetoothCallQualityReport.EVENT_BLUETOOTH_CALL_QUALITY_REPORT.equals(event) 3698 && extras.containsKey( 3699 BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT)) { 3700 notifyBluetoothCallQualityReport(extras.getParcelable( 3701 BluetoothCallQualityReport.EXTRA_BLUETOOTH_CALL_QUALITY_REPORT 3702 )); 3703 } 3704 Log.addEvent(this, LogUtils.Events.CALL_EVENT, event); 3705 sendEventToService(this, event, extras); 3706 } else { 3707 if (mFlags.cacheCallEvents()) { 3708 Log.i(this, "sendCallEvent: caching call event for callId=%s, event=%s", 3709 getId(), event); 3710 cacheServiceCallback(new CachedCallEventQueue(event, extras)); 3711 } else { 3712 Log.e(this, new NullPointerException(), 3713 "sendCallEvent failed due to null CS callId=%s", getId()); 3714 } 3715 } 3716 } 3717 3718 /** 3719 * This method should only be called from sendCallEvent(String, Bundle). 3720 */ 3721 private void sendEventToService(Call call, String event, Bundle extras) { 3722 if (mConnectionService != null) { 3723 mConnectionService.sendCallEvent(call, event, extras); 3724 } else if (mTransactionalService != null) { 3725 mTransactionalService.sendCallEvent(call, event, extras); 3726 } 3727 } 3728 3729 /** 3730 * Notifies listeners when a bluetooth quality report is received. 3731 * @param report The bluetooth quality report. 3732 */ 3733 void notifyBluetoothCallQualityReport(@NonNull BluetoothCallQualityReport report) { 3734 Log.addEvent(this, LogUtils.Events.BT_QUALITY_REPORT, "choppy=" + report.isChoppyVoice()); 3735 for (Listener l : mListeners) { 3736 l.onBluetoothCallQualityReport(this, report); 3737 } 3738 } 3739 3740 /** 3741 * Initiates a handover of this Call to the {@link ConnectionService} identified 3742 * by destAcct. 3743 * @param destAcct ConnectionService to which the call should be handed over. 3744 * @param videoState The video state desired after the handover. 3745 * @param extras Extra information to be passed to ConnectionService 3746 */ 3747 public void handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras) { 3748 requestHandover(destAcct, videoState, extras, false); 3749 } 3750 3751 /** 3752 * Sets this {@link Call} to has the specified {@code parentCall}. Also sets the parent to 3753 * have this call as a child. 3754 * @param parentCall 3755 */ 3756 void setParentAndChildCall(Call parentCall) { 3757 boolean isParentChanging = (mParentCall != parentCall); 3758 setParentCall(parentCall); 3759 setChildOf(parentCall); 3760 if (isParentChanging) { 3761 notifyParentChanged(parentCall); 3762 } 3763 } 3764 3765 /** 3766 * Notifies listeners when the parent call changes. 3767 * Used by {@link #setParentAndChildCall(Call)}, and in {@link CallsManager}. 3768 * @param parentCall The new parent call for this call. 3769 */ 3770 void notifyParentChanged(Call parentCall) { 3771 Log.addEvent(this, LogUtils.Events.SET_PARENT, parentCall); 3772 for (Listener l : mListeners) { 3773 l.onParentChanged(this); 3774 } 3775 } 3776 3777 /** 3778 * Unlike {@link #setParentAndChildCall(Call)}, only sets the parent call but does NOT set 3779 * the child. 3780 * TODO: This is only required when adding existing connections as a workaround so that we 3781 * can avoid sending the "onParentChanged" callback until later. 3782 * @param parentCall The new parent call. 3783 */ 3784 void setParentCall(Call parentCall) { 3785 if (parentCall == this) { 3786 Log.e(this, new Exception(), "setting the parent to self"); 3787 return; 3788 } 3789 if (parentCall == mParentCall) { 3790 // nothing to do 3791 return; 3792 } 3793 if (mParentCall != null) { 3794 mParentCall.removeChildCall(this); 3795 } 3796 mParentCall = parentCall; 3797 } 3798 3799 /** 3800 * To be called after {@link #setParentCall(Call)} to complete setting the parent by adding 3801 * this call as a child of another call. 3802 * <p> 3803 * Note: if using this method alone, the caller must call {@link #notifyParentChanged(Call)} to 3804 * ensure the InCall UI is updated with the change in parent. 3805 * @param parentCall The new parent for this call. 3806 */ 3807 public void setChildOf(Call parentCall) { 3808 if (parentCall != null && !parentCall.getChildCalls().contains(this)) { 3809 parentCall.addChildCall(this); 3810 } 3811 } 3812 3813 void setConferenceableCalls(List<Call> conferenceableCalls) { 3814 mConferenceableCalls.clear(); 3815 mConferenceableCalls.addAll(conferenceableCalls); 3816 String confCallIds = ""; 3817 if (!conferenceableCalls.isEmpty()) { 3818 confCallIds = conferenceableCalls.stream() 3819 .map(c -> c.getId()) 3820 .collect(Collectors.joining(",")); 3821 } 3822 Log.addEvent(this, LogUtils.Events.CONF_CALLS_CHANGED, confCallIds); 3823 3824 for (Listener l : mListeners) { 3825 l.onConferenceableCallsChanged(this); 3826 } 3827 } 3828 3829 @VisibleForTesting 3830 public List<Call> getConferenceableCalls() { 3831 return mConferenceableCalls; 3832 } 3833 3834 @VisibleForTesting 3835 public boolean can(int capability) { 3836 return (getConnectionCapabilities() & capability) == capability; 3837 } 3838 3839 @VisibleForTesting 3840 public boolean hasProperty(int property) { 3841 return (mConnectionProperties & property) == property; 3842 } 3843 3844 private void addChildCall(Call call) { 3845 if (!mChildCalls.contains(call)) { 3846 mHadChildren = true; 3847 // Set the pseudo-active call to the latest child added to the conference. 3848 // See definition of mConferenceLevelActiveCall for more detail. 3849 mConferenceLevelActiveCall = call; 3850 mChildCalls.add(call); 3851 3852 // When adding a child, we will potentially adjust the various times from the calls 3853 // based on the children being added. This ensures the parent of the conference has a 3854 // connect time reflective of all the children added. 3855 maybeAdjustConnectTime(call); 3856 3857 Log.addEvent(this, LogUtils.Events.ADD_CHILD, call); 3858 3859 for (Listener l : mListeners) { 3860 l.onChildrenChanged(this); 3861 } 3862 } 3863 } 3864 3865 /** 3866 * Potentially adjust the connect and creation time of this call based on another one. 3867 * Ensures that if the other call has an earlier connect time that we adjust the connect time of 3868 * this call to match. 3869 * <p> 3870 * This is important for conference calls; as we add children to the conference we need to 3871 * ensure that earlier connect time is reflected on the conference. In the past this 3872 * was just done in {@link ParcelableCallUtils} when parceling the calls to the UI, but that 3873 * approach would not reflect the right time on the parent as children disconnect. 3874 * 3875 * @param call the call to potentially use to adjust connect time. 3876 */ 3877 private void maybeAdjustConnectTime(@NonNull Call call) { 3878 long childConnectTimeMillis = call.getConnectTimeMillis(); 3879 long currentConnectTimeMillis = getConnectTimeMillis(); 3880 // Conference calls typically have a 0 connect time, so we will replace the current connect 3881 // time if its zero also. 3882 if (childConnectTimeMillis != 0 3883 && (currentConnectTimeMillis == 0 3884 || childConnectTimeMillis < getConnectTimeMillis())) { 3885 setConnectTimeMillis(childConnectTimeMillis); 3886 } 3887 } 3888 3889 private void removeChildCall(Call call) { 3890 if (mChildCalls.remove(call)) { 3891 Log.addEvent(this, LogUtils.Events.REMOVE_CHILD, call); 3892 for (Listener l : mListeners) { 3893 l.onChildrenChanged(this); 3894 } 3895 } 3896 } 3897 3898 /** 3899 * Return whether the user can respond to this {@code Call} via an SMS message. 3900 * 3901 * @return true if the "Respond via SMS" feature should be enabled 3902 * for this incoming call. 3903 * 3904 * The general rule is that we *do* allow "Respond via SMS" except for 3905 * the few (relatively rare) cases where we know for sure it won't 3906 * work, namely: 3907 * - a bogus or blank incoming number 3908 * - a call from a SIP address 3909 * - a "call presentation" that doesn't allow the number to be revealed 3910 * 3911 * In all other cases, we allow the user to respond via SMS. 3912 * 3913 * Note that this behavior isn't perfect; for example we have no way 3914 * to detect whether the incoming call is from a landline (with most 3915 * networks at least), so we still enable this feature even though 3916 * SMSes to that number will silently fail. 3917 */ 3918 public boolean isRespondViaSmsCapable() { 3919 if (mContext.getResources().getBoolean(R.bool.skip_loading_canned_text_response)) { 3920 Log.d(this, "maybeLoadCannedSmsResponses: skip loading due to setting"); 3921 return false; 3922 } 3923 3924 if (mState != CallState.RINGING) { 3925 return false; 3926 } 3927 3928 if (getHandle() == null) { 3929 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in 3930 // other words, the user should not be able to see the incoming phone number. 3931 return false; 3932 } 3933 3934 if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) { 3935 // The incoming number is actually a URI (i.e. a SIP address), 3936 // not a regular PSTN phone number, and we can't send SMSes to 3937 // SIP addresses. 3938 // (TODO: That might still be possible eventually, though. Is 3939 // there some SIP-specific equivalent to sending a text message?) 3940 return false; 3941 } 3942 3943 // Is there a valid SMS application on the phone? 3944 try { 3945 if (mContext.getSystemService(TelephonyManager.class) 3946 .getAndUpdateDefaultRespondViaMessageApplication() == null) { 3947 return false; 3948 } 3949 } catch (UnsupportedOperationException uoe) { 3950 return false; 3951 } 3952 3953 // TODO: with some carriers (in certain countries) you *can* actually 3954 // tell whether a given number is a mobile phone or not. So in that 3955 // case we could potentially return false here if the incoming call is 3956 // from a land line. 3957 3958 // If none of the above special cases apply, it's OK to enable the 3959 // "Respond via SMS" feature. 3960 return true; 3961 } 3962 3963 List<String> getCannedSmsResponses() { 3964 return mCannedSmsResponses; 3965 } 3966 3967 /** 3968 * We need to make sure that before we move a call to the disconnected state, it no 3969 * longer has any parent/child relationships. We want to do this to ensure that the InCall 3970 * Service always has the right data in the right order. We also want to do it in telecom so 3971 * that the insurance policy lives in the framework side of things. 3972 */ 3973 private void fixParentAfterDisconnect() { 3974 setParentAndChildCall(null); 3975 } 3976 3977 /** 3978 * @return True if the call is ringing, else logs the action name. 3979 */ 3980 private boolean isRinging(String actionName) { 3981 if (mState == CallState.RINGING || mState == CallState.ANSWERED) { 3982 return true; 3983 } 3984 3985 Log.i(this, "Request to %s a non-ringing call %s", actionName, this); 3986 return false; 3987 } 3988 3989 /** 3990 * @return True if the call is answered, else logs the action name. 3991 */ 3992 private boolean isAnswered(String actionName) { 3993 if (mState == CallState.ANSWERED) { 3994 return true; 3995 } 3996 3997 Log.i(this, "Request to %s a non-answered call %s", actionName, this); 3998 return false; 3999 } 4000 4001 @SuppressWarnings("rawtypes") 4002 private void decrementAssociatedCallCount(ServiceBinder binder) { 4003 if (binder != null) { 4004 binder.decrementAssociatedCallCount(); 4005 } 4006 } 4007 4008 /** 4009 * Looks up contact information based on the current handle. 4010 */ 4011 private void startCallerInfoLookup() { 4012 mCallerInfo = null; 4013 mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener); 4014 } 4015 4016 /** 4017 * Saves the specified caller info if the specified token matches that of the last query 4018 * that was made. 4019 * 4020 * @param callerInfo The new caller information to set. 4021 */ 4022 private void setCallerInfo(Uri handle, CallerInfo callerInfo) { 4023 if (callerInfo == null) { 4024 Log.i(this, "CallerInfo lookup returned null, skipping update"); 4025 return; 4026 } 4027 4028 if ((handle != null) && !handle.equals(mHandle)) { 4029 Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring."); 4030 return; 4031 } 4032 4033 String newName = callerInfo.getName(); 4034 boolean contactNameChanged = mCallerInfo == null || 4035 !Objects.equals(mCallerInfo.getName(), newName); 4036 4037 mCallerInfo = callerInfo; 4038 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo); 4039 4040 if (mCallerInfo.getContactDisplayPhotoUri() == null || mCallerInfo.cachedPhotoIcon != null 4041 || mCallerInfo.cachedPhoto != null || contactNameChanged) { 4042 for (Listener l : mListeners) { 4043 l.onCallerInfoChanged(this); 4044 } 4045 } 4046 } 4047 4048 public CallerInfo getCallerInfo() { 4049 return mCallerInfo; 4050 } 4051 4052 private void maybeLoadCannedSmsResponses() { 4053 if (mCallDirection == CALL_DIRECTION_INCOMING 4054 && isRespondViaSmsCapable() 4055 && !mCannedSmsResponsesLoadingStarted) { 4056 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages"); 4057 mCannedSmsResponsesLoadingStarted = true; 4058 mCallsManager.getRespondViaSmsManager().loadCannedTextMessages( 4059 new CallsManager.Response<Void, List<String>>() { 4060 @Override 4061 public void onResult(Void request, List<String>... result) { 4062 if (result.length > 0) { 4063 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]); 4064 mCannedSmsResponses = result[0]; 4065 for (Listener l : mListeners) { 4066 l.onCannedSmsResponsesLoaded(Call.this); 4067 } 4068 } 4069 } 4070 4071 @Override 4072 public void onError(Void request, int code, String msg) { 4073 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code, 4074 msg); 4075 } 4076 }, 4077 mContext 4078 ); 4079 } else { 4080 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing"); 4081 } 4082 } 4083 4084 /** 4085 * Sets speakerphone option on when call begins. 4086 */ 4087 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) { 4088 mSpeakerphoneOn = startWithSpeakerphone; 4089 } 4090 4091 /** 4092 * Returns speakerphone option. 4093 * 4094 * @return Whether or not speakerphone should be set automatically when call begins. 4095 */ 4096 public boolean getStartWithSpeakerphoneOn() { 4097 return mSpeakerphoneOn; 4098 } 4099 4100 public void setRequestedToStartWithRtt() { 4101 mDidRequestToStartWithRtt = true; 4102 } 4103 4104 public void stopRtt() { 4105 if (mTransactionalService != null) { 4106 Log.i(this, "stopRtt: called on TransactionalService. doing nothing"); 4107 } else if (mConnectionService != null) { 4108 Log.addEvent(this, LogUtils.Events.REQUEST_RTT, "stop"); 4109 mConnectionService.stopRtt(this); 4110 } else { 4111 // If this gets called by the in-call app before the connection service is set, we'll 4112 // just ignore it since it's really not supposed to happen. 4113 Log.w(this, "stopRtt() called before connection service is set."); 4114 } 4115 } 4116 4117 public void sendRttRequest() { 4118 if (mTransactionalService != null) { 4119 Log.i(this, "sendRttRequest: called on TransactionalService. doing nothing"); 4120 return; 4121 } 4122 Log.addEvent(this, LogUtils.Events.REQUEST_RTT, "start"); 4123 createRttStreams(); 4124 mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs()); 4125 } 4126 4127 private boolean areRttStreamsInitialized() { 4128 return mInCallToConnectionServiceStreams != null 4129 && mConnectionServiceToInCallStreams != null; 4130 } 4131 4132 public void createRttStreams() { 4133 if (!areRttStreamsInitialized()) { 4134 Log.i(this, "Initializing RTT streams"); 4135 try { 4136 mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe(); 4137 mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe(); 4138 } catch (IOException e) { 4139 Log.e(this, e, "Failed to create pipes for RTT call."); 4140 } 4141 } 4142 } 4143 4144 public void onRttConnectionFailure(int reason) { 4145 Log.i(this, "Got RTT initiation failure with reason %d", reason); 4146 Log.addEvent(this, LogUtils.Events.ON_RTT_FAILED, "reason=" + reason); 4147 for (Listener l : mListeners) { 4148 l.onRttInitiationFailure(this, reason); 4149 } 4150 } 4151 4152 public void onRemoteRttRequest() { 4153 Log.addEvent(this, LogUtils.Events.ON_RTT_REQUEST); 4154 if (isRttCall()) { 4155 Log.w(this, "Remote RTT request on a call that's already RTT"); 4156 return; 4157 } 4158 4159 mPendingRttRequestId = mCallsManager.getNextRttRequestId(); 4160 for (Listener l : mListeners) { 4161 l.onRemoteRttRequest(this, mPendingRttRequestId); 4162 } 4163 } 4164 4165 public void handleRttRequestResponse(int id, boolean accept) { 4166 if (mPendingRttRequestId == INVALID_RTT_REQUEST_ID) { 4167 Log.w(this, "Response received to a nonexistent RTT request: %d", id); 4168 return; 4169 } 4170 if (id != mPendingRttRequestId) { 4171 Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId); 4172 return; 4173 } 4174 if (mTransactionalService != null) { 4175 Log.i(this, "handleRttRequestResponse: called on TransactionalService. doing nothing"); 4176 return; 4177 } 4178 Log.addEvent(this, LogUtils.Events.RESPOND_TO_RTT_REQUEST, "id=" + id + ", accept=" 4179 + accept); 4180 if (accept) { 4181 createRttStreams(); 4182 Log.i(this, "RTT request %d accepted.", id); 4183 mConnectionService.respondToRttRequest( 4184 this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs()); 4185 } else { 4186 Log.i(this, "RTT request %d rejected.", id); 4187 mConnectionService.respondToRttRequest(this, null, null); 4188 } 4189 } 4190 4191 public boolean isRttCall() { 4192 return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT; 4193 } 4194 4195 public boolean wasEverRttCall() { 4196 return mWasEverRtt; 4197 } 4198 4199 public ParcelFileDescriptor getCsToInCallRttPipeForCs() { 4200 return mConnectionServiceToInCallStreams == null ? null 4201 : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX]; 4202 } 4203 4204 public ParcelFileDescriptor getInCallToCsRttPipeForCs() { 4205 return mInCallToConnectionServiceStreams == null ? null 4206 : mInCallToConnectionServiceStreams[RTT_PIPE_READ_SIDE_INDEX]; 4207 } 4208 4209 public ParcelFileDescriptor getCsToInCallRttPipeForInCall() { 4210 return mConnectionServiceToInCallStreams == null ? null 4211 : mConnectionServiceToInCallStreams[RTT_PIPE_READ_SIDE_INDEX]; 4212 } 4213 4214 public ParcelFileDescriptor getInCallToCsRttPipeForInCall() { 4215 return mInCallToConnectionServiceStreams == null ? null 4216 : mInCallToConnectionServiceStreams[RTT_PIPE_WRITE_SIDE_INDEX]; 4217 } 4218 4219 public int getRttMode() { 4220 return mRttMode; 4221 } 4222 4223 /** 4224 * Sets a video call provider for the call. 4225 */ 4226 public void setVideoProvider(IVideoProvider videoProvider) { 4227 Log.v(this, "setVideoProvider"); 4228 4229 if (mVideoProviderProxy != null) { 4230 mVideoProviderProxy.clearVideoCallback(); 4231 mVideoProviderProxy = null; 4232 } 4233 4234 if (videoProvider != null ) { 4235 try { 4236 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this, 4237 mCallsManager); 4238 } catch (RemoteException ignored) { 4239 // Ignore RemoteException. 4240 } 4241 } 4242 4243 for (Listener l : mListeners) { 4244 l.onVideoCallProviderChanged(Call.this); 4245 } 4246 } 4247 4248 /** 4249 * @return The {@link Connection.VideoProvider} binder. 4250 */ 4251 public IVideoProvider getVideoProvider() { 4252 if (mVideoProviderProxy == null) { 4253 return null; 4254 } 4255 4256 return mVideoProviderProxy.getInterface(); 4257 } 4258 4259 /** 4260 * @return The {@link VideoProviderProxy} for this call. 4261 */ 4262 public VideoProviderProxy getVideoProviderProxy() { 4263 return mVideoProviderProxy; 4264 } 4265 4266 /** 4267 * The current video state for the call. 4268 * See {@link VideoProfile} for a list of valid video states. 4269 */ 4270 public int getVideoState() { 4271 return mVideoState; 4272 } 4273 4274 /** 4275 * Returns the video states which were applicable over the duration of a call. 4276 * See {@link VideoProfile} for a list of valid video states. 4277 * 4278 * @return The video states applicable over the duration of the call. 4279 */ 4280 public int getVideoStateHistory() { 4281 return mVideoStateHistory; 4282 } 4283 4284 /** 4285 * Determines the current video state for the call. 4286 * For an outgoing call determines the desired video state for the call. 4287 * Valid values: see {@link VideoProfile} 4288 * 4289 * @param videoState The video state for the call. 4290 */ 4291 public void setVideoState(int videoState) { 4292 // If the phone account associated with this call does not support video calling, then we 4293 // will automatically set the video state to audio-only. 4294 if (!isVideoCallingSupportedByPhoneAccount()) { 4295 Log.d(this, "setVideoState: videoState=%s defaulted to audio (video not supported)", 4296 VideoProfile.videoStateToString(videoState)); 4297 videoState = VideoProfile.STATE_AUDIO_ONLY; 4298 } 4299 4300 // TODO:: b/338280297. If a transactional call does not have the 4301 // CallAttributes.SUPPORTS_VIDEO_CALLING capability, the videoState should be set to audio 4302 4303 // Track Video State history during the duration of the call. 4304 // Only update the history when the call is active or disconnected. This ensures we do 4305 // not include the video state history when: 4306 // - Call is incoming (but not answered). 4307 // - Call it outgoing (but not answered). 4308 // We include the video state when disconnected to ensure that rejected calls reflect the 4309 // appropriate video state. 4310 // For all other times we add to the video state history, see #setState. 4311 if (isActive() || getState() == CallState.DISCONNECTED) { 4312 mVideoStateHistory = mVideoStateHistory | videoState; 4313 } 4314 4315 int previousVideoState = mVideoState; 4316 mVideoState = videoState; 4317 if (mVideoState != previousVideoState) { 4318 if (!mIsTransactionalCall) { 4319 Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED, 4320 VideoProfile.videoStateToString(videoState)); 4321 } 4322 for (Listener l : mListeners) { 4323 l.onVideoStateChanged(this, previousVideoState, mVideoState); 4324 } 4325 } 4326 4327 if (mFlags.transactionalVideoState() && mIsTransactionalCall) { 4328 int transactionalVS = VideoProfileStateToTransactionalVideoState(mVideoState); 4329 if (mTransactionalService != null) { 4330 Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED, 4331 TransactionalVideoStateToString(transactionalVS)); 4332 mTransactionalService.onVideoStateChanged(this, transactionalVS); 4333 } else { 4334 cacheServiceCallback(new CachedVideoStateChange(transactionalVS)); 4335 } 4336 } 4337 4338 if (VideoProfile.isVideo(videoState)) { 4339 mHasVideoCall = true; 4340 mAnalytics.setCallIsVideo(true); 4341 } 4342 } 4343 4344 public boolean getIsVoipAudioMode() { 4345 return mIsVoipAudioMode; 4346 } 4347 4348 public void setIsVoipAudioMode(boolean audioModeIsVoip) { 4349 if (isSelfManaged() && !audioModeIsVoip) { 4350 Log.i(this, 4351 "setIsVoipAudioMode: ignoring request to set self-managed audio to " 4352 + "non-voip mode"); 4353 return; 4354 } 4355 if (mIsVoipAudioMode != audioModeIsVoip) { 4356 Log.addEvent(this, LogUtils.Events.SET_VOIP_MODE, audioModeIsVoip ? "Y" : "N"); 4357 } 4358 mIsVoipAudioMode = audioModeIsVoip; 4359 for (Listener l : mListeners) { 4360 l.onIsVoipAudioModeChanged(this); 4361 } 4362 } 4363 4364 public StatusHints getStatusHints() { 4365 return mStatusHints; 4366 } 4367 4368 public void setStatusHints(StatusHints statusHints) { 4369 mStatusHints = statusHints; 4370 for (Listener l : mListeners) { 4371 l.onStatusHintsChanged(this); 4372 } 4373 } 4374 4375 public boolean isUnknown() { 4376 return mCallDirection == CALL_DIRECTION_UNKNOWN; 4377 } 4378 4379 public boolean isOutgoing() { 4380 return mCallDirection == CALL_DIRECTION_OUTGOING; 4381 } 4382 4383 /** 4384 * Determines if this call is in a disconnecting state. 4385 * 4386 * @return {@code true} if this call is locally disconnecting. 4387 */ 4388 public boolean isLocallyDisconnecting() { 4389 return mIsLocallyDisconnecting; 4390 } 4391 4392 /** 4393 * Sets whether this call is in a disconnecting state. 4394 * 4395 * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting. 4396 */ 4397 private void setLocallyDisconnecting(boolean isLocallyDisconnecting) { 4398 mIsLocallyDisconnecting = isLocallyDisconnecting; 4399 } 4400 4401 /** 4402 * It's possible that the target phone account isn't set when a user hasn't selected a 4403 * default sim to place a call. Instead of using the user from the target phone account to 4404 * associate the user with a call, we'll use mAssociatedUser instead. For MT calls, we will 4405 * continue to use the target phone account user (as it's always set) and for MO calls, we will 4406 * use the initiating user instead. 4407 * 4408 * @return user handle of user associated with the call. 4409 */ 4410 public UserHandle getAssociatedUser() { 4411 return mAssociatedUser; 4412 } 4413 4414 /** 4415 * Set the user handle of user associated with the call. 4416 * @param associatedUser 4417 */ 4418 public void setAssociatedUser(UserHandle associatedUser) { 4419 Log.i(this, "Setting associated user for call: %s", associatedUser); 4420 Preconditions.checkNotNull(associatedUser); 4421 mAssociatedUser = associatedUser; 4422 } 4423 4424 static int getStateFromConnectionState(int state) { 4425 switch (state) { 4426 case Connection.STATE_INITIALIZING: 4427 return CallState.CONNECTING; 4428 case Connection.STATE_ACTIVE: 4429 return CallState.ACTIVE; 4430 case Connection.STATE_DIALING: 4431 return CallState.DIALING; 4432 case Connection.STATE_PULLING_CALL: 4433 return CallState.PULLING; 4434 case Connection.STATE_DISCONNECTED: 4435 return CallState.DISCONNECTED; 4436 case Connection.STATE_HOLDING: 4437 return CallState.ON_HOLD; 4438 case Connection.STATE_NEW: 4439 return CallState.NEW; 4440 case Connection.STATE_RINGING: 4441 return CallState.RINGING; 4442 } 4443 return CallState.DISCONNECTED; 4444 } 4445 4446 /** 4447 * Determines if this call is in disconnected state and waiting to be destroyed. 4448 * 4449 * @return {@code true} if this call is disconected. 4450 */ 4451 public boolean isDisconnected() { 4452 return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED); 4453 } 4454 4455 /** 4456 * Determines if this call has just been created and has not been configured properly yet. 4457 * 4458 * @return {@code true} if this call is new. 4459 */ 4460 public boolean isNew() { 4461 return getState() == CallState.NEW; 4462 } 4463 4464 /** 4465 * Sets the call data usage for the call. 4466 * 4467 * @param callDataUsage The new call data usage (in bytes). 4468 */ 4469 public void setCallDataUsage(long callDataUsage) { 4470 mCallDataUsage = callDataUsage; 4471 } 4472 4473 /** 4474 * Returns the call data usage for the call. 4475 * 4476 * @return The call data usage (in bytes). 4477 */ 4478 public long getCallDataUsage() { 4479 return mCallDataUsage; 4480 } 4481 4482 public void setRttMode(int mode) { 4483 mRttMode = mode; 4484 Log.addEvent(this, LogUtils.Events.SET_RRT_MODE, "mode=" + mode); 4485 // TODO: hook this up to CallAudioManager. 4486 } 4487 4488 /** 4489 * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent 4490 * has come back to telecom and was processed. 4491 */ 4492 public boolean isNewOutgoingCallIntentBroadcastDone() { 4493 return mIsNewOutgoingCallIntentBroadcastDone; 4494 } 4495 4496 public void setNewOutgoingCallIntentBroadcastIsDone() { 4497 mIsNewOutgoingCallIntentBroadcastDone = true; 4498 } 4499 4500 /** 4501 * Determines if the call has been held by the remote party. 4502 * 4503 * @return {@code true} if the call is remotely held, {@code false} otherwise. 4504 */ 4505 public boolean isRemotelyHeld() { 4506 return mIsRemotelyHeld; 4507 } 4508 4509 /** 4510 * Handles Connection events received from a {@link ConnectionService}. 4511 * 4512 * @param event The event. 4513 * @param extras The extras. 4514 */ 4515 public void onConnectionEvent(String event, Bundle extras) { 4516 if (mIsTransactionalCall) { 4517 // send the Event directly to the ICS via the InCallController listener 4518 for (Listener l : mListeners) { 4519 l.onConnectionEvent(this, event, extras); 4520 } 4521 // Don't run the below block since it applies to Calls that are attached to a 4522 // ConnectionService 4523 return; 4524 } 4525 // Don't log call quality reports; they're quite frequent and will clog the log. 4526 if (!Connection.EVENT_CALL_QUALITY_REPORT.equals(event)) { 4527 Log.addEvent(this, LogUtils.Events.CONNECTION_EVENT, event); 4528 } 4529 if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) { 4530 mIsRemotelyHeld = true; 4531 Log.addEvent(this, LogUtils.Events.REMOTELY_HELD); 4532 // Inform listeners of the fact that a call hold tone was received. This will trigger 4533 // the CallAudioManager to play a tone via the InCallTonePlayer. 4534 for (Listener l : mListeners) { 4535 l.onHoldToneRequested(this); 4536 } 4537 } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) { 4538 mIsRemotelyHeld = false; 4539 Log.addEvent(this, LogUtils.Events.REMOTELY_UNHELD); 4540 for (Listener l : mListeners) { 4541 l.onHoldToneRequested(this); 4542 } 4543 } else if (Connection.EVENT_CALL_HOLD_FAILED.equals(event)) { 4544 for (Listener l : mListeners) { 4545 l.onCallHoldFailed(this); 4546 } 4547 } else if (Connection.EVENT_CALL_SWITCH_FAILED.equals(event)) { 4548 for (Listener l : mListeners) { 4549 l.onCallSwitchFailed(this); 4550 } 4551 } else if (Connection.EVENT_CALL_RESUME_FAILED.equals(event)) { 4552 for (Listener l : mListeners) { 4553 l.onCallResumeFailed(this); 4554 } 4555 } else if (Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE.equals(event) 4556 && extras != null && extras.containsKey( 4557 Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE) 4558 && extras.containsKey(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE)) { 4559 // Relay an incoming D2D message to interested listeners; most notably the 4560 // CallDiagnosticService. 4561 int messageType = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE); 4562 int messageValue = extras.getInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE); 4563 for (Listener l : mListeners) { 4564 l.onReceivedDeviceToDeviceMessage(this, messageType, messageValue); 4565 } 4566 } else if (Connection.EVENT_CALL_QUALITY_REPORT.equals(event) 4567 && extras != null && extras.containsKey(Connection.EXTRA_CALL_QUALITY_REPORT)) { 4568 CallQuality callQuality = extras.getParcelable(Connection.EXTRA_CALL_QUALITY_REPORT); 4569 for (Listener l : mListeners) { 4570 l.onReceivedCallQualityReport(this, callQuality); 4571 } 4572 } else { 4573 if (event.equals(EVENT_DISPLAY_EMERGENCY_MESSAGE) && !isEmergencyCall()) { 4574 Log.w(this, "onConnectionEvent: EVENT_DISPLAY_EMERGENCY_MESSAGE is sent " 4575 + "without an emergency call"); 4576 return; 4577 } 4578 4579 for (Listener l : mListeners) { 4580 l.onConnectionEvent(this, event, extras); 4581 } 4582 } 4583 } 4584 4585 /** 4586 * Notifies interested parties that the handover has completed. 4587 * Notifies: 4588 * 1. {@link InCallController} which communicates this to the 4589 * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}. 4590 * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of 4591 * the successful handover. 4592 */ 4593 public void onHandoverComplete() { 4594 Log.i(this, "onHandoverComplete; callId=%s", getId()); 4595 if (mConnectionService != null) { 4596 mConnectionService.handoverComplete(this); 4597 } 4598 for (Listener l : mListeners) { 4599 l.onHandoverComplete(this); 4600 } 4601 } 4602 4603 public void onHandoverFailed(int handoverError) { 4604 Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError); 4605 for (Listener l : mListeners) { 4606 l.onHandoverFailed(this, handoverError); 4607 } 4608 } 4609 4610 public void setOriginalConnectionId(String originalConnectionId) { 4611 mOriginalConnectionId = originalConnectionId; 4612 } 4613 4614 /** 4615 * For calls added via a ConnectionManager using the 4616 * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle, 4617 * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS, 4618 * indicates the ID of this call as it was referred to by the {@code ConnectionService} which 4619 * originally created it. 4620 * 4621 * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}. 4622 * @return The original connection ID. 4623 */ 4624 public String getOriginalConnectionId() { 4625 return mOriginalConnectionId; 4626 } 4627 4628 public ConnectionServiceFocusManager getConnectionServiceFocusManager() { 4629 return mCallsManager.getConnectionServiceFocusManager(); 4630 } 4631 4632 /** 4633 * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either 4634 * remotely or locally. 4635 * 4636 * @param capabilities The {@link Connection} capabilities for the call. 4637 * @return {@code true} if video is supported, {@code false} otherwise. 4638 */ 4639 private boolean doesCallSupportVideo(int capabilities) { 4640 return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 || 4641 (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0; 4642 } 4643 4644 /** 4645 * Remove any video capabilities set on a {@link Connection} capabilities bitmask. 4646 * 4647 * @param capabilities The capabilities. 4648 * @return The bitmask with video capabilities removed. 4649 */ 4650 private int removeVideoCapabilities(int capabilities) { 4651 return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL | 4652 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 4653 } 4654 4655 /** 4656 * Initiates a handover of this {@link Call} to another {@link PhoneAccount}. 4657 * @param handoverToHandle The {@link PhoneAccountHandle} to handover to. 4658 * @param videoState The video state of the call when handed over. 4659 * @param extras Optional extras {@link Bundle} provided by the initiating 4660 * {@link android.telecom.InCallService}. 4661 */ 4662 private void requestHandover(PhoneAccountHandle handoverToHandle, int videoState, 4663 Bundle extras, boolean isLegacy) { 4664 for (Listener l : mListeners) { 4665 l.onHandoverRequested(this, handoverToHandle, videoState, extras, isLegacy); 4666 } 4667 } 4668 4669 private TelephonyManager getTelephonyManager() { 4670 return mContext.getSystemService(TelephonyManager.class); 4671 } 4672 4673 /** 4674 * Sets whether this {@link Call} is a conference or not. 4675 * @param isConference 4676 */ 4677 public void setConferenceState(boolean isConference) { 4678 mIsConference = isConference; 4679 Log.addEvent(this, LogUtils.Events.CONF_STATE_CHANGED, "isConference=" + isConference); 4680 // Ultimately CallsManager needs to know so it can update the "add call" state and inform 4681 // the UI to update itself. 4682 for (Listener l : mListeners) { 4683 l.onConferenceStateChanged(this, isConference); 4684 } 4685 } 4686 4687 /** 4688 * Change the call direction. This is useful if it was not previously defined (for example in 4689 * single caller emulation mode). 4690 * @param callDirection The new direction of this call. 4691 */ 4692 // Make sure the callDirection has been mapped to the Call definition correctly! 4693 public void setCallDirection(int callDirection) { 4694 if (mCallDirection != callDirection) { 4695 Log.addEvent(this, LogUtils.Events.CALL_DIRECTION_CHANGED, "callDirection=" 4696 + callDirection); 4697 mCallDirection = callDirection; 4698 for (Listener l : mListeners) { 4699 // Update InCallService directly, do not notify CallsManager. 4700 l.onCallDirectionChanged(this); 4701 } 4702 } 4703 } 4704 4705 /** 4706 * Sets the video history based on the state and state transitions of the call. Always add the 4707 * current video state to the video state history during a call transition except for the 4708 * transitions DIALING->ACTIVE and RINGING->ANSWERED. In these cases, clear the history. If a 4709 * call starts dialing/ringing as a VT call and gets downgraded to audio, we need to record 4710 * the history as an audio call. 4711 */ 4712 private void updateVideoHistoryViaState(int oldState, int newState) { 4713 if ((oldState == CallState.DIALING && newState == CallState.ACTIVE) 4714 || (oldState == CallState.RINGING && newState == CallState.ANSWERED)) { 4715 mVideoStateHistory = mVideoState; 4716 } 4717 4718 mVideoStateHistory |= mVideoState; 4719 } 4720 4721 /** 4722 * Returns whether or not high definition audio was used. 4723 * 4724 * @return true if high definition audio was used during this call. 4725 */ 4726 boolean wasHighDefAudio() { 4727 return mWasHighDefAudio; 4728 } 4729 4730 /** 4731 * Returns whether or not Wifi call was used. 4732 * 4733 * @return true if wifi call was used during this call. 4734 */ 4735 boolean wasWifi() { 4736 return mWasWifi; 4737 } 4738 4739 public void setIsUsingCallFiltering(boolean isUsingCallFiltering) { 4740 mIsUsingCallFiltering = isUsingCallFiltering; 4741 } 4742 4743 public boolean isUsingCallFiltering() { 4744 return mIsUsingCallFiltering; 4745 } 4746 4747 /** 4748 * Returns whether or not Volte call was used. 4749 * 4750 * @return true if Volte call was used during this call. 4751 */ 4752 public boolean wasVolte() { 4753 return mWasVolte; 4754 } 4755 4756 /** 4757 * In some cases, we need to know if this call has ever gone active (for example, the case 4758 * when the call was put into the {@link CallState#AUDIO_PROCESSING} state after being active) 4759 * for call logging purposes. 4760 * 4761 * @return {@code true} if this call has gone active before (even if it isn't now), false if it 4762 * has never gone active. 4763 */ 4764 public boolean hasGoneActiveBefore() { 4765 return mHasGoneActiveBefore; 4766 } 4767 4768 /** 4769 * When upgrading a call to video via 4770 * {@link VideoProviderProxy#onSendSessionModifyRequest(VideoProfile, VideoProfile)}, if the 4771 * upgrade is from audio to video, potentially auto-engage the speakerphone. 4772 * @param newVideoState The proposed new video state for the call. 4773 */ 4774 public void maybeEnableSpeakerForVideoUpgrade(@VideoProfile.VideoState int newVideoState) { 4775 if (mCallsManager.isSpeakerphoneAutoEnabledForVideoCalls(newVideoState)) { 4776 Log.i(this, "maybeEnableSpeakerForVideoCall; callId=%s, auto-enable speaker for call" 4777 + " upgraded to video."); 4778 mCallsManager.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null); 4779 } 4780 } 4781 4782 /** 4783 * Sends a device to device message to the other part of the call. 4784 * @param message the message type to send. 4785 * @param value the value for the message. 4786 */ 4787 public void sendDeviceToDeviceMessage(@CallDiagnostics.MessageType int message, int value) { 4788 Log.i(this, "sendDeviceToDeviceMessage; callId=%s, msg=%d/%d", getId(), message, value); 4789 Bundle extras = new Bundle(); 4790 extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_TYPE, message); 4791 extras.putInt(Connection.EXTRA_DEVICE_TO_DEVICE_MESSAGE_VALUE, value); 4792 // Send to the connection service. 4793 sendCallEvent(Connection.EVENT_DEVICE_TO_DEVICE_MESSAGE, extras); 4794 } 4795 4796 /** 4797 * Signals to the Dialer app to start displaying a diagnostic message. 4798 * @param messageId a unique ID for the message to display. 4799 * @param message the message to display. 4800 */ 4801 public void displayDiagnosticMessage(int messageId, @NonNull CharSequence message) { 4802 Bundle extras = new Bundle(); 4803 extras.putInt(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE_ID, messageId); 4804 extras.putCharSequence(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE, message); 4805 // Send to the dialer. 4806 onConnectionEvent(android.telecom.Call.EVENT_DISPLAY_DIAGNOSTIC_MESSAGE, extras); 4807 } 4808 4809 /** 4810 * Signals to the Dialer app to stop displaying a diagnostic message. 4811 * @param messageId a unique ID for the message to clear. 4812 */ 4813 public void clearDiagnosticMessage(int messageId) { 4814 Bundle extras = new Bundle(); 4815 extras.putInt(android.telecom.Call.EXTRA_DIAGNOSTIC_MESSAGE_ID, messageId); 4816 // Send to the dialer. 4817 onConnectionEvent(android.telecom.Call.EVENT_CLEAR_DIAGNOSTIC_MESSAGE, extras); 4818 } 4819 4820 /** 4821 * Remaps the call direction as indicated by an {@link android.telecom.Call.Details} direction 4822 * constant to the constants (e.g. {@link #CALL_DIRECTION_INCOMING}) used in this call class. 4823 * @param direction The android.telecom.Call direction. 4824 * @return The direction using the constants in this class. 4825 */ 4826 public static int getRemappedCallDirection( 4827 @android.telecom.Call.Details.CallDirection int direction) { 4828 switch(direction) { 4829 case android.telecom.Call.Details.DIRECTION_INCOMING: 4830 return CALL_DIRECTION_INCOMING; 4831 case android.telecom.Call.Details.DIRECTION_OUTGOING: 4832 return CALL_DIRECTION_OUTGOING; 4833 case android.telecom.Call.Details.DIRECTION_UNKNOWN: 4834 return CALL_DIRECTION_UNDEFINED; 4835 } 4836 return CALL_DIRECTION_UNDEFINED; 4837 } 4838 4839 /** 4840 * Set the package name of the {@link android.telecom.CallScreeningService} which should be sent 4841 * the {@link android.telecom.TelecomManager#ACTION_POST_CALL} upon disconnection of a call. 4842 * @param packageName post call screen service package name. 4843 */ 4844 public void setPostCallPackageName(String packageName) { 4845 mPostCallPackageName = packageName; 4846 } 4847 4848 /** 4849 * Return the package name of the {@link android.telecom.CallScreeningService} which should be 4850 * sent the {@link android.telecom.TelecomManager#ACTION_POST_CALL} upon disconnection of a 4851 * call. 4852 * @return post call screen service package name. 4853 */ 4854 public String getPostCallPackageName() { 4855 return mPostCallPackageName; 4856 } 4857 4858 public long getMissedReason() { 4859 return mMissedReason; 4860 } 4861 4862 public void setMissedReason(long missedReason) { 4863 mMissedReason = missedReason; 4864 } 4865 4866 public void setUserMissed(long code) { 4867 mMissedReason |= code; 4868 } 4869 4870 public long getStartRingTime() { 4871 return mStartRingTime; 4872 } 4873 4874 public void setStartRingTime() { 4875 mStartRingTime = mClockProxy.elapsedRealtime(); 4876 } 4877 4878 public CharSequence getCallScreeningAppName() { 4879 return mCallScreeningAppName; 4880 } 4881 4882 public void setCallScreeningAppName(CharSequence callScreeningAppName) { 4883 mCallScreeningAppName = callScreeningAppName; 4884 } 4885 4886 public String getCallScreeningComponentName() { 4887 return mCallScreeningComponentName; 4888 } 4889 4890 public void setCallScreeningComponentName(String callScreeningComponentName) { 4891 mCallScreeningComponentName = callScreeningComponentName; 4892 } 4893 4894 public void setStartFailCause(CallFailureCause cause) { 4895 Log.i(this, "setStartFailCause: cause = %s; callId = %s", cause, this.getId()); 4896 mCallStateChangedAtomWriter.setStartFailCause(cause); 4897 } 4898 4899 public void increaseHeldByThisCallCount() { 4900 mCallStateChangedAtomWriter.increaseHeldCallCount(); 4901 } 4902 4903 public void maybeOnInCallServiceTrackingChanged(boolean isTracking, boolean hasUi) { 4904 if (mTransactionalService != null) { 4905 Log.i(this, 4906 "maybeOnInCallServiceTrackingChanged: called on TransactionalService"); 4907 return; 4908 } 4909 if (mConnectionService == null) { 4910 Log.w(this, "maybeOnInCallServiceTrackingChanged() request on a call" 4911 + " without a connection service."); 4912 } else { 4913 if (hasUi) { 4914 mConnectionService.onUsingAlternativeUi(this, isTracking); 4915 } else if (isTracking) { 4916 mConnectionService.onTrackedByNonUiService(this, isTracking); 4917 } 4918 } 4919 } 4920 4921 /** 4922 * @return {@code true} when this call originated from a SIM-based {@link PhoneAccount}. 4923 * A sim-based {@link PhoneAccount} is one with {@link PhoneAccount#CAPABILITY_SIM_SUBSCRIPTION} 4924 * set. 4925 */ 4926 public boolean isSimCall() { 4927 return mIsSimCall; 4928 } 4929 4930 /** 4931 * Sets whether this is a sim call or not. 4932 * @param isSimCall {@code true} if this is a SIM call, {@code false} otherwise. 4933 */ 4934 public void setIsSimCall(boolean isSimCall) { 4935 mIsSimCall = isSimCall; 4936 } 4937 4938 /** 4939 * Initializes a disconnect future which is used to chain up pending operations which take 4940 * place when the {@link CallDiagnosticService} returns the result of the 4941 * {@link CallDiagnostics#onCallDisconnected(int, int)} or 4942 * {@link CallDiagnostics#onCallDisconnected(ImsReasonInfo)} invocation via 4943 * {@link CallDiagnosticServiceAdapter}. If no {@link CallDiagnosticService} is in use, we 4944 * would not try to make a disconnect future. 4945 * @param timeoutMillis Timeout we use for waiting for the response. 4946 * @return the {@link CompletableFuture}. 4947 */ 4948 public CompletableFuture<Boolean> initializeDiagnosticCompleteFuture(long timeoutMillis) { 4949 if (mDiagnosticCompleteFuture == null) { 4950 mDiagnosticCompleteFuture = new CompletableFuture<Boolean>() 4951 .completeOnTimeout(false, timeoutMillis, TimeUnit.MILLISECONDS); 4952 // After all the chained stuff we will report where the CDS timed out. 4953 mDiagnosticCompleteFuture.thenRunAsync(() -> { 4954 if (!mReceivedCallDiagnosticPostCallResponse) { 4955 Log.addEvent(this, LogUtils.Events.CALL_DIAGNOSTIC_SERVICE_TIMEOUT); 4956 } 4957 // Clear the future as a final step. 4958 mDiagnosticCompleteFuture = null; 4959 }, 4960 new LoggedHandlerExecutor(mHandler, "C.iDF", mLock)) 4961 .exceptionally((throwable) -> { 4962 Log.e(this, throwable, "Error while executing disconnect future"); 4963 return null; 4964 }); 4965 } 4966 return mDiagnosticCompleteFuture; 4967 } 4968 4969 /** 4970 * @return the disconnect future, if initialized. Used for chaining operations after creation. 4971 */ 4972 public CompletableFuture<Boolean> getDiagnosticCompleteFuture() { 4973 return mDiagnosticCompleteFuture; 4974 } 4975 4976 /** 4977 * @return {@code true} if disconnection and removal is handled via a future, or {@code false} 4978 * if this is handled immediately. 4979 */ 4980 public boolean isDisconnectHandledViaFuture() { 4981 return mDiagnosticCompleteFuture != null; 4982 } 4983 4984 /** 4985 * Perform any cleanup on this call as a result of a {@link TelecomServiceImpl} 4986 * {@code cleanupStuckCalls} request. 4987 */ 4988 public void cleanup() { 4989 if (mDiagnosticCompleteFuture != null) { 4990 mDiagnosticCompleteFuture.complete(false); 4991 mDiagnosticCompleteFuture = null; 4992 } 4993 } 4994 4995 /** 4996 * Set the pending future to use when the call is disconnected. 4997 */ 4998 public void setDisconnectFuture(CompletableFuture<Void> future) { 4999 mDisconnectFuture = future; 5000 } 5001 5002 /** 5003 * @return The future that will be executed when the call is disconnected. 5004 */ 5005 public CompletableFuture<Void> getDisconnectFuture() { 5006 return mDisconnectFuture; 5007 } 5008 5009 /** 5010 * Set the future that will be used when call removal is taking place. 5011 */ 5012 public void setRemovalFuture(CompletableFuture<Void> future) { 5013 mRemovalFuture = future; 5014 } 5015 5016 /** 5017 * @return {@code true} if there is a pending removal operation that hasn't taken place yet, or 5018 * {@code false} if there is no removal pending. 5019 */ 5020 public boolean isRemovalPending() { 5021 return mRemovalFuture != null && !mRemovalFuture.isDone(); 5022 } 5023 5024 /** 5025 * Set the bluetooth {@link android.telecom.InCallService} binding completion or timeout future 5026 * which is used to delay the audio routing change after the bluetooth stack get notified about 5027 * the ringing calls. 5028 * @param btIcsFuture the {@link CompletableFuture} 5029 */ 5030 public void setBtIcsFuture(CompletableFuture<Boolean> btIcsFuture) { 5031 mBtIcsFuture = btIcsFuture; 5032 } 5033 5034 /** 5035 * @return The binding {@link CompletableFuture} for the BT ICS. 5036 */ 5037 public CompletableFuture<Boolean> getBtIcsFuture() { 5038 return mBtIcsFuture; 5039 } 5040 5041 /** 5042 * Wait for bluetooth {@link android.telecom.InCallService} binding completion or timeout. Used 5043 * for audio routing operations for a ringing call. 5044 */ 5045 public void waitForBtIcs() { 5046 if (mBtIcsFuture != null) { 5047 try { 5048 Log.i(this, "waitForBtIcs: waiting for BT service to bind"); 5049 mBtIcsFuture.get(); 5050 } catch (InterruptedException | ExecutionException e) { 5051 // ignore 5052 } 5053 } 5054 } 5055 5056 /** 5057 * @return {@code true} if the connection has been created by the underlying 5058 * {@link ConnectionService}, {@code false} otherwise. 5059 */ 5060 public boolean isCreateConnectionComplete() { 5061 return mIsCreateConnectionComplete; 5062 } 5063 5064 @VisibleForTesting 5065 public void setIsCreateConnectionComplete(boolean isCreateConnectionComplete) { 5066 mIsCreateConnectionComplete = isCreateConnectionComplete; 5067 } 5068 5069 public boolean isStreaming() { 5070 synchronized (mLock) { 5071 return mIsStreaming; 5072 } 5073 } 5074 5075 public void startStreaming() { 5076 if (!mIsTransactionalCall) { 5077 throw new UnsupportedOperationException( 5078 "Can't streaming call created by non voip apps"); 5079 } 5080 Log.addEvent(this, LogUtils.Events.START_STREAMING); 5081 synchronized (mLock) { 5082 if (mIsStreaming) { 5083 // ignore 5084 return; 5085 } 5086 5087 mIsStreaming = true; 5088 for (Listener listener : mListeners) { 5089 listener.onCallStreamingStateChanged(this, true /** isStreaming */); 5090 } 5091 } 5092 } 5093 5094 public void stopStreaming() { 5095 synchronized (mLock) { 5096 if (!mIsStreaming) { 5097 // ignore 5098 return; 5099 } 5100 Log.addEvent(this, LogUtils.Events.STOP_STREAMING); 5101 mIsStreaming = false; 5102 for (Listener listener : mListeners) { 5103 listener.onCallStreamingStateChanged(this, false /** isStreaming */); 5104 } 5105 } 5106 } 5107 5108 public void setSimultaneousType(int simultaneousType) { 5109 mSimultaneousType = simultaneousType; 5110 } 5111 5112 public int getSimultaneousType() { 5113 return mSimultaneousType; 5114 } 5115 5116 public boolean hasVideoCall() { 5117 return mHasVideoCall; 5118 } 5119 5120 /** 5121 * Used only for call sequencing for cases when we may end up auto-unholding the held call while 5122 * processing an outgoing (emergency) call. We want to refrain from unholding the held call so 5123 * that we don't end up with two active calls. Once the outgoing call is disconnected (either 5124 * from a successful disconnect by the user or a failed call), the auto-unhold logic will be 5125 * triggered again and successfully unhold the held call at that point. Note, that this only 5126 * applies to non-holdable phone accounts (i.e. Verizon). Refer to 5127 * {@link CallsManagerCallSequencingAdapter#maybeMoveHeldCallToForeground} for details. 5128 */ 5129 public void setSkipAutoUnhold(boolean result) { 5130 mSkipAutoUnhold = result; 5131 } 5132 5133 public boolean getSkipAutoUnhold() { 5134 return mSkipAutoUnhold; 5135 } 5136 } 5137