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