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