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