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