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