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