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