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