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