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