Call.java revision 77ff680018a1d75963c697f835827a2966bd42c1
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.telecom; 18 19import android.content.Context; 20import android.content.Intent; 21import android.graphics.Bitmap; 22import android.graphics.drawable.Drawable; 23import android.net.Uri; 24import android.os.Build; 25import android.os.Bundle; 26import android.os.Handler; 27import android.os.Looper; 28import android.os.ParcelFileDescriptor; 29import android.os.Parcelable; 30import android.os.RemoteException; 31import android.os.SystemClock; 32import android.os.Trace; 33import android.provider.ContactsContract.Contacts; 34import android.telecom.CallAudioState; 35import android.telecom.Conference; 36import android.telecom.ConnectionService; 37import android.telecom.DisconnectCause; 38import android.telecom.Connection; 39import android.telecom.GatewayInfo; 40import android.telecom.Log; 41import android.telecom.Logging.EventManager; 42import android.telecom.ParcelableConnection; 43import android.telecom.PhoneAccount; 44import android.telecom.PhoneAccountHandle; 45import android.telecom.Response; 46import android.telecom.StatusHints; 47import android.telecom.TelecomManager; 48import android.telecom.VideoProfile; 49import android.telephony.PhoneNumberUtils; 50import android.text.TextUtils; 51import android.util.StatsLog; 52import android.os.UserHandle; 53 54import com.android.internal.annotations.VisibleForTesting; 55import com.android.internal.telecom.IVideoProvider; 56import com.android.internal.telephony.CallerInfo; 57import com.android.internal.telephony.SmsApplication; 58import com.android.internal.util.Preconditions; 59 60import java.io.IOException; 61import java.lang.String; 62import java.text.SimpleDateFormat; 63import java.util.ArrayList; 64import java.util.Collections; 65import java.util.Date; 66import java.util.LinkedList; 67import java.util.List; 68import java.util.Locale; 69import java.util.Objects; 70import java.util.Set; 71import 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 79public 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 return phoneAccount.getExtras() != null && phoneAccount.getExtras().getBoolean( 1176 PhoneAccount.EXTRA_LOG_SELF_MANAGED_CALLS, false); 1177 } 1178 1179 @VisibleForTesting 1180 public boolean isIncoming() { 1181 return mCallDirection == CALL_DIRECTION_INCOMING; 1182 } 1183 1184 public boolean isExternalCall() { 1185 return (getConnectionProperties() & Connection.PROPERTY_IS_EXTERNAL_CALL) == 1186 Connection.PROPERTY_IS_EXTERNAL_CALL; 1187 } 1188 1189 public boolean isWorkCall() { 1190 return mIsWorkCall; 1191 } 1192 1193 public boolean isUsingCallRecordingTone() { 1194 return mUseCallRecordingTone; 1195 } 1196 1197 public boolean isVideoCallingSupported() { 1198 return mIsVideoCallingSupported; 1199 } 1200 1201 public boolean isSelfManaged() { 1202 return mIsSelfManaged; 1203 } 1204 1205 public void setIsSelfManaged(boolean isSelfManaged) { 1206 mIsSelfManaged = isSelfManaged; 1207 1208 // Connection properties will add/remove the PROPERTY_SELF_MANAGED. 1209 setConnectionProperties(getConnectionProperties()); 1210 } 1211 1212 public void markFinishedHandoverStateAndCleanup(int handoverState) { 1213 if (mHandoverSourceCall != null) { 1214 mHandoverSourceCall.setHandoverState(handoverState); 1215 } else if (mHandoverDestinationCall != null) { 1216 mHandoverDestinationCall.setHandoverState(handoverState); 1217 } 1218 setHandoverState(handoverState); 1219 maybeCleanupHandover(); 1220 } 1221 1222 public void maybeCleanupHandover() { 1223 if (mHandoverSourceCall != null) { 1224 mHandoverSourceCall.setHandoverSourceCall(null); 1225 mHandoverSourceCall.setHandoverDestinationCall(null); 1226 mHandoverSourceCall = null; 1227 } else if (mHandoverDestinationCall != null) { 1228 mHandoverDestinationCall.setHandoverSourceCall(null); 1229 mHandoverDestinationCall.setHandoverDestinationCall(null); 1230 mHandoverDestinationCall = null; 1231 } 1232 } 1233 1234 public boolean isHandoverInProgress() { 1235 return mHandoverSourceCall != null || mHandoverDestinationCall != null; 1236 } 1237 1238 public Call getHandoverDestinationCall() { 1239 return mHandoverDestinationCall; 1240 } 1241 1242 public void setHandoverDestinationCall(Call call) { 1243 mHandoverDestinationCall = call; 1244 } 1245 1246 public Call getHandoverSourceCall() { 1247 return mHandoverSourceCall; 1248 } 1249 1250 public void setHandoverSourceCall(Call call) { 1251 mHandoverSourceCall = call; 1252 } 1253 1254 public void setHandoverState(int handoverState) { 1255 Log.d(this, "setHandoverState: callId=%s, handoverState=%s", getId(), 1256 HandoverState.stateToString(handoverState)); 1257 mHandoverState = handoverState; 1258 } 1259 1260 public int getHandoverState() { 1261 return mHandoverState; 1262 } 1263 1264 private void configureCallAttributes() { 1265 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar(); 1266 boolean isWorkCall = false; 1267 boolean isCallRecordingToneSupported = false; 1268 PhoneAccount phoneAccount = 1269 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle); 1270 if (phoneAccount != null) { 1271 final UserHandle userHandle; 1272 if (phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) { 1273 userHandle = mInitiatingUser; 1274 } else { 1275 userHandle = mTargetPhoneAccountHandle.getUserHandle(); 1276 } 1277 if (userHandle != null) { 1278 isWorkCall = UserUtil.isManagedProfile(mContext, userHandle); 1279 } 1280 1281 isCallRecordingToneSupported = (phoneAccount.hasCapabilities( 1282 PhoneAccount.CAPABILITY_SIM_SUBSCRIPTION) && phoneAccount.getExtras() != null 1283 && phoneAccount.getExtras().getBoolean( 1284 PhoneAccount.EXTRA_PLAY_CALL_RECORDING_TONE, false)); 1285 } 1286 mIsWorkCall = isWorkCall; 1287 mUseCallRecordingTone = isCallRecordingToneSupported; 1288 } 1289 1290 /** 1291 * Caches the state of the {@link PhoneAccount#CAPABILITY_VIDEO_CALLING} {@link PhoneAccount} 1292 * capability and ensures that the video state is updated if the phone account does not support 1293 * video calling. 1294 */ 1295 private void checkIfVideoCapable() { 1296 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar(); 1297 if (mTargetPhoneAccountHandle == null) { 1298 // If no target phone account handle is specified, assume we can potentially perform a 1299 // video call; once the phone account is set, we can confirm that it is video capable. 1300 mIsVideoCallingSupported = true; 1301 Log.d(this, "checkIfVideoCapable: no phone account selected; assume video capable."); 1302 return; 1303 } 1304 PhoneAccount phoneAccount = 1305 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle); 1306 mIsVideoCallingSupported = phoneAccount != null && phoneAccount.hasCapabilities( 1307 PhoneAccount.CAPABILITY_VIDEO_CALLING); 1308 1309 if (!mIsVideoCallingSupported && VideoProfile.isVideo(getVideoState())) { 1310 // The PhoneAccount for the Call was set to one which does not support video calling, 1311 // and the current call is configured to be a video call; downgrade to audio-only. 1312 setVideoState(VideoProfile.STATE_AUDIO_ONLY); 1313 Log.d(this, "checkIfVideoCapable: selected phone account doesn't support video."); 1314 } 1315 } 1316 1317 private void checkIfRttCapable() { 1318 PhoneAccountRegistrar phoneAccountRegistrar = mCallsManager.getPhoneAccountRegistrar(); 1319 if (mTargetPhoneAccountHandle == null) { 1320 return; 1321 } 1322 1323 // Check both the target phone account and the connection manager phone account -- if 1324 // either support RTT, just set the streams and have them set/unset the RTT property as 1325 // needed. 1326 PhoneAccount phoneAccount = 1327 phoneAccountRegistrar.getPhoneAccountUnchecked(mTargetPhoneAccountHandle); 1328 PhoneAccount connectionManagerPhoneAccount = phoneAccountRegistrar.getPhoneAccountUnchecked( 1329 mConnectionManagerPhoneAccountHandle); 1330 boolean isRttSupported = phoneAccount != null && phoneAccount.hasCapabilities( 1331 PhoneAccount.CAPABILITY_RTT); 1332 boolean isConnectionManagerRttSupported = connectionManagerPhoneAccount != null 1333 && connectionManagerPhoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_RTT); 1334 1335 if ((isConnectionManagerRttSupported || isRttSupported) 1336 && mDidRequestToStartWithRtt && !areRttStreamsInitialized()) { 1337 // If the phone account got set to an RTT capable one and we haven't set the streams 1338 // yet, do so now. 1339 createRttStreams(); 1340 Log.i(this, "Setting RTT streams after target phone account selected"); 1341 } 1342 } 1343 1344 boolean shouldAttachToExistingConnection() { 1345 return mShouldAttachToExistingConnection; 1346 } 1347 1348 /** 1349 * Note: This method relies on {@link #mConnectElapsedTimeMillis} and 1350 * {@link #mDisconnectElapsedTimeMillis} which are independent of the wall clock (which could 1351 * change due to clock changes). 1352 * @return The "age" of this call object in milliseconds, which typically also represents the 1353 * period since this call was added to the set pending outgoing calls. 1354 */ 1355 @VisibleForTesting 1356 public long getAgeMillis() { 1357 if (mState == CallState.DISCONNECTED && 1358 (mDisconnectCause.getCode() == DisconnectCause.REJECTED || 1359 mDisconnectCause.getCode() == DisconnectCause.MISSED)) { 1360 // Rejected and missed calls have no age. They're immortal!! 1361 return 0; 1362 } else if (mConnectElapsedTimeMillis == 0) { 1363 // Age is measured in the amount of time the call was active. A zero connect time 1364 // indicates that we never went active, so return 0 for the age. 1365 return 0; 1366 } else if (mDisconnectElapsedTimeMillis == 0) { 1367 // We connected, but have not yet disconnected 1368 return mClockProxy.elapsedRealtime() - mConnectElapsedTimeMillis; 1369 } 1370 1371 return mDisconnectElapsedTimeMillis - mConnectElapsedTimeMillis; 1372 } 1373 1374 /** 1375 * @return The time when this call object was created and added to the set of pending outgoing 1376 * calls. 1377 */ 1378 public long getCreationTimeMillis() { 1379 return mCreationTimeMillis; 1380 } 1381 1382 public void setCreationTimeMillis(long time) { 1383 mCreationTimeMillis = time; 1384 } 1385 1386 long getConnectTimeMillis() { 1387 return mConnectTimeMillis; 1388 } 1389 1390 int getConnectionCapabilities() { 1391 return mConnectionCapabilities; 1392 } 1393 1394 int getConnectionProperties() { 1395 return mConnectionProperties; 1396 } 1397 1398 void setConnectionCapabilities(int connectionCapabilities) { 1399 setConnectionCapabilities(connectionCapabilities, false /* forceUpdate */); 1400 } 1401 1402 void setConnectionCapabilities(int connectionCapabilities, boolean forceUpdate) { 1403 Log.v(this, "setConnectionCapabilities: %s", Connection.capabilitiesToString( 1404 connectionCapabilities)); 1405 if (forceUpdate || mConnectionCapabilities != connectionCapabilities) { 1406 // If the phone account does not support video calling, and the connection capabilities 1407 // passed in indicate that the call supports video, remove those video capabilities. 1408 if (!isVideoCallingSupported() && doesCallSupportVideo(connectionCapabilities)) { 1409 Log.w(this, "setConnectionCapabilities: attempt to set connection as video " + 1410 "capable when not supported by the phone account."); 1411 connectionCapabilities = removeVideoCapabilities(connectionCapabilities); 1412 } 1413 1414 int previousCapabilities = mConnectionCapabilities; 1415 mConnectionCapabilities = connectionCapabilities; 1416 for (Listener l : mListeners) { 1417 l.onConnectionCapabilitiesChanged(this); 1418 } 1419 1420 int xorCaps = previousCapabilities ^ mConnectionCapabilities; 1421 Log.addEvent(this, LogUtils.Events.CAPABILITY_CHANGE, 1422 "Current: [%s], Removed [%s], Added [%s]", 1423 Connection.capabilitiesToStringShort(mConnectionCapabilities), 1424 Connection.capabilitiesToStringShort(previousCapabilities & xorCaps), 1425 Connection.capabilitiesToStringShort(mConnectionCapabilities & xorCaps)); 1426 } 1427 } 1428 1429 void setConnectionProperties(int connectionProperties) { 1430 Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString( 1431 connectionProperties)); 1432 1433 // Ensure the ConnectionService can't change the state of the self-managed property. 1434 if (isSelfManaged()) { 1435 connectionProperties |= Connection.PROPERTY_SELF_MANAGED; 1436 } else { 1437 connectionProperties &= ~Connection.PROPERTY_SELF_MANAGED; 1438 } 1439 1440 int changedProperties = mConnectionProperties ^ connectionProperties; 1441 1442 if (changedProperties != 0) { 1443 int previousProperties = mConnectionProperties; 1444 mConnectionProperties = connectionProperties; 1445 if ((mConnectionProperties & Connection.PROPERTY_IS_RTT) == 1446 Connection.PROPERTY_IS_RTT) { 1447 createRttStreams(); 1448 if (isEmergencyCall()) { 1449 mCallsManager.setAudioRoute(CallAudioState.ROUTE_SPEAKER, null); 1450 mCallsManager.mute(false); 1451 } 1452 } 1453 mWasHighDefAudio = (connectionProperties & Connection.PROPERTY_HIGH_DEF_AUDIO) == 1454 Connection.PROPERTY_HIGH_DEF_AUDIO; 1455 boolean didRttChange = 1456 (changedProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT; 1457 for (Listener l : mListeners) { 1458 l.onConnectionPropertiesChanged(this, didRttChange); 1459 } 1460 1461 boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL) 1462 == Connection.PROPERTY_IS_EXTERNAL_CALL; 1463 boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL) 1464 == Connection.PROPERTY_IS_EXTERNAL_CALL; 1465 if (wasExternal != isExternal) { 1466 Log.v(this, "setConnectionProperties: external call changed isExternal = %b", 1467 isExternal); 1468 Log.addEvent(this, LogUtils.Events.IS_EXTERNAL, isExternal); 1469 for (Listener l : mListeners) { 1470 l.onExternalCallChanged(this, isExternal); 1471 } 1472 } 1473 1474 mAnalytics.addCallProperties(mConnectionProperties); 1475 1476 int xorProps = previousProperties ^ mConnectionProperties; 1477 Log.addEvent(this, LogUtils.Events.PROPERTY_CHANGE, 1478 "Current: [%s], Removed [%s], Added [%s]", 1479 Connection.propertiesToStringShort(mConnectionProperties), 1480 Connection.propertiesToStringShort(previousProperties & xorProps), 1481 Connection.propertiesToStringShort(mConnectionProperties & xorProps)); 1482 } 1483 } 1484 1485 public int getSupportedAudioRoutes() { 1486 return mSupportedAudioRoutes; 1487 } 1488 1489 void setSupportedAudioRoutes(int audioRoutes) { 1490 if (mSupportedAudioRoutes != audioRoutes) { 1491 mSupportedAudioRoutes = audioRoutes; 1492 } 1493 } 1494 1495 @VisibleForTesting 1496 public Call getParentCall() { 1497 return mParentCall; 1498 } 1499 1500 @VisibleForTesting 1501 public List<Call> getChildCalls() { 1502 return mChildCalls; 1503 } 1504 1505 @VisibleForTesting 1506 public boolean wasConferencePreviouslyMerged() { 1507 return mWasConferencePreviouslyMerged; 1508 } 1509 1510 public boolean isDisconnectingChildCall() { 1511 return mIsDisconnectingChildCall; 1512 } 1513 1514 /** 1515 * Sets whether this call is a child call. 1516 */ 1517 private void maybeSetCallAsDisconnectingChild() { 1518 if (mParentCall != null) { 1519 mIsDisconnectingChildCall = true; 1520 } 1521 } 1522 1523 @VisibleForTesting 1524 public Call getConferenceLevelActiveCall() { 1525 return mConferenceLevelActiveCall; 1526 } 1527 1528 @VisibleForTesting 1529 public ConnectionServiceWrapper getConnectionService() { 1530 return mConnectionService; 1531 } 1532 1533 /** 1534 * Retrieves the {@link Context} for the call. 1535 * 1536 * @return The {@link Context}. 1537 */ 1538 public Context getContext() { 1539 return mContext; 1540 } 1541 1542 @VisibleForTesting 1543 public void setConnectionService(ConnectionServiceWrapper service) { 1544 Preconditions.checkNotNull(service); 1545 1546 clearConnectionService(); 1547 1548 service.incrementAssociatedCallCount(); 1549 mConnectionService = service; 1550 mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString()); 1551 mConnectionService.addCall(this); 1552 } 1553 1554 /** 1555 * Perform an in-place replacement of the {@link ConnectionServiceWrapper} for this Call. 1556 * Removes the call from its former {@link ConnectionServiceWrapper}, ensuring that the 1557 * ConnectionService is NOT unbound if the call count hits zero. 1558 * This is used by the {@link ConnectionServiceWrapper} when handling {@link Connection} and 1559 * {@link Conference} additions via a ConnectionManager. 1560 * The original {@link android.telecom.ConnectionService} will directly add external calls and 1561 * conferences to Telecom as well as the ConnectionManager, which will add to Telecom. In these 1562 * cases since its first added to via the original CS, we want to change the CS responsible for 1563 * the call to the ConnectionManager rather than adding it again as another call/conference. 1564 * 1565 * @param service The new {@link ConnectionServiceWrapper}. 1566 */ 1567 public void replaceConnectionService(ConnectionServiceWrapper service) { 1568 Preconditions.checkNotNull(service); 1569 1570 if (mConnectionService != null) { 1571 ConnectionServiceWrapper serviceTemp = mConnectionService; 1572 mConnectionService = null; 1573 serviceTemp.removeCall(this); 1574 serviceTemp.decrementAssociatedCallCount(true /*isSuppressingUnbind*/); 1575 } 1576 1577 service.incrementAssociatedCallCount(); 1578 mConnectionService = service; 1579 mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString()); 1580 } 1581 1582 /** 1583 * Clears the associated connection service. 1584 */ 1585 void clearConnectionService() { 1586 if (mConnectionService != null) { 1587 ConnectionServiceWrapper serviceTemp = mConnectionService; 1588 mConnectionService = null; 1589 serviceTemp.removeCall(this); 1590 1591 // Decrementing the count can cause the service to unbind, which itself can trigger the 1592 // service-death code. Since the service death code tries to clean up any associated 1593 // calls, we need to make sure to remove that information (e.g., removeCall()) before 1594 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is 1595 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good 1596 // to do. 1597 decrementAssociatedCallCount(serviceTemp); 1598 } 1599 } 1600 1601 /** 1602 * Starts the create connection sequence. Upon completion, there should exist an active 1603 * connection through a connection service (or the call will have failed). 1604 * 1605 * @param phoneAccountRegistrar The phone account registrar. 1606 */ 1607 void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { 1608 if (mCreateConnectionProcessor != null) { 1609 Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" + 1610 " due to a race between NewOutgoingCallIntentBroadcaster and " + 1611 "phoneAccountSelected, but is harmlessly resolved by ignoring the second " + 1612 "invocation."); 1613 return; 1614 } 1615 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, 1616 phoneAccountRegistrar, mContext); 1617 mCreateConnectionProcessor.process(); 1618 } 1619 1620 @Override 1621 public void handleCreateConnectionSuccess( 1622 CallIdMapper idMapper, 1623 ParcelableConnection connection) { 1624 Log.v(this, "handleCreateConnectionSuccessful %s", connection); 1625 setTargetPhoneAccount(connection.getPhoneAccount()); 1626 setHandle(connection.getHandle(), connection.getHandlePresentation()); 1627 setCallerDisplayName( 1628 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation()); 1629 1630 setConnectionCapabilities(connection.getConnectionCapabilities()); 1631 setConnectionProperties(connection.getConnectionProperties()); 1632 setIsVoipAudioMode(connection.getIsVoipAudioMode()); 1633 setSupportedAudioRoutes(connection.getSupportedAudioRoutes()); 1634 setVideoProvider(connection.getVideoProvider()); 1635 setVideoState(connection.getVideoState()); 1636 setRingbackRequested(connection.isRingbackRequested()); 1637 setStatusHints(connection.getStatusHints()); 1638 putExtras(SOURCE_CONNECTION_SERVICE, connection.getExtras()); 1639 1640 mConferenceableCalls.clear(); 1641 for (String id : connection.getConferenceableConnectionIds()) { 1642 mConferenceableCalls.add(idMapper.getCall(id)); 1643 } 1644 1645 switch (mCallDirection) { 1646 case CALL_DIRECTION_INCOMING: 1647 // Listeners (just CallsManager for now) will be responsible for checking whether 1648 // the call should be blocked. 1649 for (Listener l : mListeners) { 1650 l.onSuccessfulIncomingCall(this); 1651 } 1652 break; 1653 case CALL_DIRECTION_OUTGOING: 1654 for (Listener l : mListeners) { 1655 l.onSuccessfulOutgoingCall(this, 1656 getStateFromConnectionState(connection.getState())); 1657 } 1658 break; 1659 case CALL_DIRECTION_UNKNOWN: 1660 for (Listener l : mListeners) { 1661 l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection 1662 .getState())); 1663 } 1664 break; 1665 } 1666 } 1667 1668 @Override 1669 public void handleCreateConnectionFailure(DisconnectCause disconnectCause) { 1670 clearConnectionService(); 1671 setDisconnectCause(disconnectCause); 1672 mCallsManager.markCallAsDisconnected(this, disconnectCause); 1673 1674 switch (mCallDirection) { 1675 case CALL_DIRECTION_INCOMING: 1676 for (Listener listener : mListeners) { 1677 listener.onFailedIncomingCall(this); 1678 } 1679 break; 1680 case CALL_DIRECTION_OUTGOING: 1681 for (Listener listener : mListeners) { 1682 listener.onFailedOutgoingCall(this, disconnectCause); 1683 } 1684 break; 1685 case CALL_DIRECTION_UNKNOWN: 1686 for (Listener listener : mListeners) { 1687 listener.onFailedUnknownCall(this); 1688 } 1689 break; 1690 } 1691 } 1692 1693 /** 1694 * Plays the specified DTMF tone. 1695 */ 1696 @VisibleForTesting 1697 public void playDtmfTone(char digit) { 1698 if (mConnectionService == null) { 1699 Log.w(this, "playDtmfTone() request on a call without a connection service."); 1700 } else { 1701 Log.i(this, "Send playDtmfTone to connection service for call %s", this); 1702 mConnectionService.playDtmfTone(this, digit); 1703 Log.addEvent(this, LogUtils.Events.START_DTMF, Log.pii(digit)); 1704 } 1705 mPlayingDtmfTone = digit; 1706 } 1707 1708 /** 1709 * Stops playing any currently playing DTMF tone. 1710 */ 1711 @VisibleForTesting 1712 public void stopDtmfTone() { 1713 if (mConnectionService == null) { 1714 Log.w(this, "stopDtmfTone() request on a call without a connection service."); 1715 } else { 1716 Log.i(this, "Send stopDtmfTone to connection service for call %s", this); 1717 Log.addEvent(this, LogUtils.Events.STOP_DTMF); 1718 mConnectionService.stopDtmfTone(this); 1719 } 1720 mPlayingDtmfTone = NO_DTMF_TONE; 1721 } 1722 1723 /** 1724 * @return {@code true} if a DTMF tone has been started via {@link #playDtmfTone(char)} but has 1725 * not been stopped via {@link #stopDtmfTone()}, {@code false} otherwise. 1726 */ 1727 boolean isDtmfTonePlaying() { 1728 return mPlayingDtmfTone != NO_DTMF_TONE; 1729 } 1730 1731 /** 1732 * Silences the ringer. 1733 */ 1734 void silence() { 1735 if (mConnectionService == null) { 1736 Log.w(this, "silence() request on a call without a connection service."); 1737 } else { 1738 Log.i(this, "Send silence to connection service for call %s", this); 1739 Log.addEvent(this, LogUtils.Events.SILENCE); 1740 mConnectionService.silence(this); 1741 } 1742 } 1743 1744 @VisibleForTesting 1745 public void disconnect() { 1746 disconnect(0); 1747 } 1748 1749 @VisibleForTesting 1750 public void disconnect(String reason) { 1751 disconnect(0, reason); 1752 } 1753 1754 /** 1755 * Attempts to disconnect the call through the connection service. 1756 */ 1757 @VisibleForTesting 1758 public void disconnect(long disconnectionTimeout) { 1759 disconnect(disconnectionTimeout, "internal" /** reason */); 1760 } 1761 1762 /** 1763 * Attempts to disconnect the call through the connection service. 1764 * @param reason the reason for the disconnect; used for logging purposes only. In some cases 1765 * this can be a package name if the disconnect was initiated through an API such 1766 * as TelecomManager. 1767 */ 1768 @VisibleForTesting 1769 public void disconnect(long disconnectionTimeout, String reason) { 1770 Log.addEvent(this, LogUtils.Events.REQUEST_DISCONNECT, reason); 1771 1772 // Track that the call is now locally disconnecting. 1773 setLocallyDisconnecting(true); 1774 maybeSetCallAsDisconnectingChild(); 1775 1776 if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT || 1777 mState == CallState.CONNECTING) { 1778 Log.v(this, "Aborting call %s", this); 1779 abort(disconnectionTimeout); 1780 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) { 1781 if (mConnectionService == null) { 1782 Log.e(this, new Exception(), "disconnect() request on a call without a" 1783 + " connection service."); 1784 } else { 1785 Log.i(this, "Send disconnect to connection service for call: %s", this); 1786 // The call isn't officially disconnected until the connection service 1787 // confirms that the call was actually disconnected. Only then is the 1788 // association between call and connection service severed, see 1789 // {@link CallsManager#markCallAsDisconnected}. 1790 mConnectionService.disconnect(this); 1791 } 1792 } 1793 } 1794 1795 void abort(long disconnectionTimeout) { 1796 if (mCreateConnectionProcessor != null && 1797 !mCreateConnectionProcessor.isProcessingComplete()) { 1798 mCreateConnectionProcessor.abort(); 1799 } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT 1800 || mState == CallState.CONNECTING) { 1801 if (disconnectionTimeout > 0) { 1802 // If the cancelation was from NEW_OUTGOING_CALL with a timeout of > 0 1803 // milliseconds, do not destroy the call. 1804 // Instead, we announce the cancellation and CallsManager handles 1805 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and 1806 // then re-dial them quickly using a gateway, allowing the first call to end 1807 // causes jank. This timeout allows CallsManager to transition the first call into 1808 // the second call so that in-call only ever sees a single call...eliminating the 1809 // jank altogether. The app will also be able to set the timeout via an extra on 1810 // the ordered broadcast. 1811 for (Listener listener : mListeners) { 1812 if (listener.onCanceledViaNewOutgoingCallBroadcast( 1813 this, disconnectionTimeout)) { 1814 // The first listener to handle this wins. A return value of true means that 1815 // the listener will handle the disconnection process later and so we 1816 // should not continue it here. 1817 setLocallyDisconnecting(false); 1818 return; 1819 } 1820 } 1821 } 1822 1823 handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED)); 1824 } else { 1825 Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING"); 1826 } 1827 } 1828 1829 /** 1830 * Answers the call if it is ringing. 1831 * 1832 * @param videoState The video state in which to answer the call. 1833 */ 1834 @VisibleForTesting 1835 public void answer(int videoState) { 1836 // Check to verify that the call is still in the ringing state. A call can change states 1837 // between the time the user hits 'answer' and Telecom receives the command. 1838 if (isRinging("answer")) { 1839 if (!isVideoCallingSupported() && VideoProfile.isVideo(videoState)) { 1840 // Video calling is not supported, yet the InCallService is attempting to answer as 1841 // video. We will simply answer as audio-only. 1842 videoState = VideoProfile.STATE_AUDIO_ONLY; 1843 } 1844 // At this point, we are asking the connection service to answer but we don't assume 1845 // that it will work. Instead, we wait until confirmation from the connectino service 1846 // that the call is in a non-STATE_RINGING state before changing the UI. See 1847 // {@link ConnectionServiceAdapter#setActive} and other set* methods. 1848 if (mConnectionService != null) { 1849 mConnectionService.answer(this, videoState); 1850 } else { 1851 Log.e(this, new NullPointerException(), 1852 "answer call failed due to null CS callId=%s", getId()); 1853 } 1854 Log.addEvent(this, LogUtils.Events.REQUEST_ACCEPT); 1855 } 1856 } 1857 1858 /** 1859 * Deflects the call if it is ringing. 1860 * 1861 * @param address address to be deflected to. 1862 */ 1863 @VisibleForTesting 1864 public void deflect(Uri address) { 1865 // Check to verify that the call is still in the ringing state. A call can change states 1866 // between the time the user hits 'deflect' and Telecomm receives the command. 1867 if (isRinging("deflect")) { 1868 // At this point, we are asking the connection service to deflect but we don't assume 1869 // that it will work. Instead, we wait until confirmation from the connection service 1870 // that the call is in a non-STATE_RINGING state before changing the UI. See 1871 // {@link ConnectionServiceAdapter#setActive} and other set* methods. 1872 mVideoStateHistory |= mVideoState; 1873 if (mConnectionService != null) { 1874 mConnectionService.deflect(this, address); 1875 } else { 1876 Log.e(this, new NullPointerException(), 1877 "deflect call failed due to null CS callId=%s", getId()); 1878 } 1879 Log.addEvent(this, LogUtils.Events.REQUEST_DEFLECT, Log.pii(address)); 1880 } 1881 } 1882 1883 /** 1884 * Rejects the call if it is ringing. 1885 * 1886 * @param rejectWithMessage Whether to send a text message as part of the call rejection. 1887 * @param textMessage An optional text message to send as part of the rejection. 1888 */ 1889 @VisibleForTesting 1890 public void reject(boolean rejectWithMessage, String textMessage) { 1891 reject(rejectWithMessage, textMessage, "internal" /** reason */); 1892 } 1893 1894 /** 1895 * Rejects the call if it is ringing. 1896 * 1897 * @param rejectWithMessage Whether to send a text message as part of the call rejection. 1898 * @param textMessage An optional text message to send as part of the rejection. 1899 * @param reason The reason for the reject; used for logging purposes. May be a package name 1900 * if the reject is initiated from an API such as TelecomManager. 1901 */ 1902 @VisibleForTesting 1903 public void reject(boolean rejectWithMessage, String textMessage, String reason) { 1904 // Check to verify that the call is still in the ringing state. A call can change states 1905 // between the time the user hits 'reject' and Telecomm receives the command. 1906 if (isRinging("reject")) { 1907 // Ensure video state history tracks video state at time of rejection. 1908 mVideoStateHistory |= mVideoState; 1909 1910 if (mConnectionService != null) { 1911 mConnectionService.reject(this, rejectWithMessage, textMessage); 1912 } else { 1913 Log.e(this, new NullPointerException(), 1914 "reject call failed due to null CS callId=%s", getId()); 1915 } 1916 Log.addEvent(this, LogUtils.Events.REQUEST_REJECT, reason); 1917 } 1918 } 1919 1920 /** 1921 * Puts the call on hold if it is currently active. 1922 */ 1923 @VisibleForTesting 1924 public void hold() { 1925 hold(null /* reason */); 1926 } 1927 1928 public void hold(String reason) { 1929 if (mState == CallState.ACTIVE) { 1930 if (mConnectionService != null) { 1931 mConnectionService.hold(this); 1932 } else { 1933 Log.e(this, new NullPointerException(), 1934 "hold call failed due to null CS callId=%s", getId()); 1935 } 1936 Log.addEvent(this, LogUtils.Events.REQUEST_HOLD, reason); 1937 } 1938 } 1939 1940 /** 1941 * Releases the call from hold if it is currently active. 1942 */ 1943 @VisibleForTesting 1944 public void unhold() { 1945 unhold(null /* reason */); 1946 } 1947 1948 public void unhold(String reason) { 1949 if (mState == CallState.ON_HOLD) { 1950 if (mConnectionService != null) { 1951 mConnectionService.unhold(this); 1952 } else { 1953 Log.e(this, new NullPointerException(), 1954 "unhold call failed due to null CS callId=%s", getId()); 1955 } 1956 Log.addEvent(this, LogUtils.Events.REQUEST_UNHOLD, reason); 1957 } 1958 } 1959 1960 /** Checks if this is a live call or not. */ 1961 @VisibleForTesting 1962 public boolean isAlive() { 1963 switch (mState) { 1964 case CallState.NEW: 1965 case CallState.RINGING: 1966 case CallState.DISCONNECTED: 1967 case CallState.ABORTED: 1968 return false; 1969 default: 1970 return true; 1971 } 1972 } 1973 1974 boolean isActive() { 1975 return mState == CallState.ACTIVE; 1976 } 1977 1978 Bundle getExtras() { 1979 return mExtras; 1980 } 1981 1982 /** 1983 * Adds extras to the extras bundle associated with this {@link Call}. 1984 * 1985 * Note: this method needs to know the source of the extras change (see 1986 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which 1987 * originate from a connection service will only be notified to incall services. Likewise, 1988 * changes originating from the incall services will only notify the connection service of the 1989 * change. 1990 * 1991 * @param source The source of the extras addition. 1992 * @param extras The extras. 1993 */ 1994 void putExtras(int source, Bundle extras) { 1995 if (extras == null) { 1996 return; 1997 } 1998 if (mExtras == null) { 1999 mExtras = new Bundle(); 2000 } 2001 mExtras.putAll(extras); 2002 2003 for (Listener l : mListeners) { 2004 l.onExtrasChanged(this, source, extras); 2005 } 2006 2007 // If the change originated from an InCallService, notify the connection service. 2008 if (source == SOURCE_INCALL_SERVICE) { 2009 if (mConnectionService != null) { 2010 mConnectionService.onExtrasChanged(this, mExtras); 2011 } else { 2012 Log.e(this, new NullPointerException(), 2013 "putExtras failed due to null CS callId=%s", getId()); 2014 } 2015 } 2016 } 2017 2018 /** 2019 * Removes extras from the extras bundle associated with this {@link Call}. 2020 * 2021 * Note: this method needs to know the source of the extras change (see 2022 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which 2023 * originate from a connection service will only be notified to incall services. Likewise, 2024 * changes originating from the incall services will only notify the connection service of the 2025 * change. 2026 * 2027 * @param source The source of the extras removal. 2028 * @param keys The extra keys to remove. 2029 */ 2030 void removeExtras(int source, List<String> keys) { 2031 if (mExtras == null) { 2032 return; 2033 } 2034 for (String key : keys) { 2035 mExtras.remove(key); 2036 } 2037 2038 for (Listener l : mListeners) { 2039 l.onExtrasRemoved(this, source, keys); 2040 } 2041 2042 // If the change originated from an InCallService, notify the connection service. 2043 if (source == SOURCE_INCALL_SERVICE) { 2044 if (mConnectionService != null) { 2045 mConnectionService.onExtrasChanged(this, mExtras); 2046 } else { 2047 Log.e(this, new NullPointerException(), 2048 "removeExtras failed due to null CS callId=%s", getId()); 2049 } 2050 } 2051 } 2052 2053 @VisibleForTesting 2054 public Bundle getIntentExtras() { 2055 return mIntentExtras; 2056 } 2057 2058 void setIntentExtras(Bundle extras) { 2059 mIntentExtras = extras; 2060 } 2061 2062 public Intent getOriginalCallIntent() { 2063 return mOriginalCallIntent; 2064 } 2065 2066 public void setOriginalCallIntent(Intent intent) { 2067 mOriginalCallIntent = intent; 2068 } 2069 2070 /** 2071 * @return the uri of the contact associated with this call. 2072 */ 2073 @VisibleForTesting 2074 public Uri getContactUri() { 2075 if (mCallerInfo == null || !mCallerInfo.contactExists) { 2076 return getHandle(); 2077 } 2078 return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey); 2079 } 2080 2081 Uri getRingtone() { 2082 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri; 2083 } 2084 2085 void onPostDialWait(String remaining) { 2086 for (Listener l : mListeners) { 2087 l.onPostDialWait(this, remaining); 2088 } 2089 } 2090 2091 void onPostDialChar(char nextChar) { 2092 for (Listener l : mListeners) { 2093 l.onPostDialChar(this, nextChar); 2094 } 2095 } 2096 2097 void postDialContinue(boolean proceed) { 2098 if (mConnectionService != null) { 2099 mConnectionService.onPostDialContinue(this, proceed); 2100 } else { 2101 Log.e(this, new NullPointerException(), 2102 "postDialContinue failed due to null CS callId=%s", getId()); 2103 } 2104 } 2105 2106 void conferenceWith(Call otherCall) { 2107 if (mConnectionService == null) { 2108 Log.w(this, "conference requested on a call without a connection service."); 2109 } else { 2110 Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH, otherCall); 2111 mConnectionService.conference(this, otherCall); 2112 } 2113 } 2114 2115 void splitFromConference() { 2116 if (mConnectionService == null) { 2117 Log.w(this, "splitting from conference call without a connection service"); 2118 } else { 2119 Log.addEvent(this, LogUtils.Events.SPLIT_FROM_CONFERENCE); 2120 mConnectionService.splitFromConference(this); 2121 } 2122 } 2123 2124 @VisibleForTesting 2125 public void mergeConference() { 2126 if (mConnectionService == null) { 2127 Log.w(this, "merging conference calls without a connection service."); 2128 } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) { 2129 Log.addEvent(this, LogUtils.Events.CONFERENCE_WITH); 2130 mConnectionService.mergeConference(this); 2131 mWasConferencePreviouslyMerged = true; 2132 } 2133 } 2134 2135 @VisibleForTesting 2136 public void swapConference() { 2137 if (mConnectionService == null) { 2138 Log.w(this, "swapping conference calls without a connection service."); 2139 } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) { 2140 Log.addEvent(this, LogUtils.Events.SWAP); 2141 mConnectionService.swapConference(this); 2142 switch (mChildCalls.size()) { 2143 case 1: 2144 mConferenceLevelActiveCall = mChildCalls.get(0); 2145 break; 2146 case 2: 2147 // swap 2148 mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ? 2149 mChildCalls.get(1) : mChildCalls.get(0); 2150 break; 2151 default: 2152 // For anything else 0, or 3+, set it to null since it is impossible to tell. 2153 mConferenceLevelActiveCall = null; 2154 break; 2155 } 2156 } 2157 } 2158 2159 /** 2160 * Initiates a request to the connection service to pull this call. 2161 * <p> 2162 * This method can only be used for calls that have the 2163 * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and 2164 * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set. 2165 * <p> 2166 * An external call is a representation of a call which is taking place on another device 2167 * associated with a PhoneAccount on this device. Issuing a request to pull the external call 2168 * tells the {@link android.telecom.ConnectionService} that it should move the call from the 2169 * other device to this one. An example of this is the IMS multi-endpoint functionality. A 2170 * user may have two phones with the same phone number. If the user is engaged in an active 2171 * call on their first device, the network will inform the second device of that ongoing call in 2172 * the form of an external call. The user may wish to continue their conversation on the second 2173 * device, so will issue a request to pull the call to the second device. 2174 * <p> 2175 * Requests to pull a call which is not external, or a call which is not pullable are ignored. 2176 */ 2177 public void pullExternalCall() { 2178 if (mConnectionService == null) { 2179 Log.w(this, "pulling a call without a connection service."); 2180 } 2181 2182 if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) { 2183 Log.w(this, "pullExternalCall - call %s is not an external call.", mId); 2184 return; 2185 } 2186 2187 if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) { 2188 Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId); 2189 return; 2190 } 2191 Log.addEvent(this, LogUtils.Events.REQUEST_PULL); 2192 mConnectionService.pullExternalCall(this); 2193 } 2194 2195 /** 2196 * Sends a call event to the {@link ConnectionService} for this call. This function is 2197 * called for event other than {@link Call#EVENT_REQUEST_HANDOVER} 2198 * 2199 * @param event The call event. 2200 * @param extras Associated extras. 2201 */ 2202 public void sendCallEvent(String event, Bundle extras) { 2203 sendCallEvent(event, 0/*For Event != EVENT_REQUEST_HANDOVER*/, extras); 2204 } 2205 2206 /** 2207 * Sends a call event to the {@link ConnectionService} for this call. 2208 * 2209 * See {@link Call#sendCallEvent(String, Bundle)}. 2210 * 2211 * @param event The call event. 2212 * @param targetSdkVer SDK version of the app calling this api 2213 * @param extras Associated extras. 2214 */ 2215 public void sendCallEvent(String event, int targetSdkVer, Bundle extras) { 2216 if (mConnectionService != null) { 2217 if (android.telecom.Call.EVENT_REQUEST_HANDOVER.equals(event)) { 2218 if (targetSdkVer > Build.VERSION_CODES.O_MR1) { 2219 Log.e(this, new Exception(), "sendCallEvent failed. Use public api handoverTo" + 2220 " for API > 27(O-MR1)"); 2221 // TODO: Add "return" after DUO team adds new API support for handover 2222 } 2223 2224 // Handover requests are targeted at Telecom, not the ConnectionService. 2225 if (extras == null) { 2226 Log.w(this, "sendCallEvent: %s event received with null extras.", 2227 android.telecom.Call.EVENT_REQUEST_HANDOVER); 2228 mConnectionService.sendCallEvent(this, 2229 android.telecom.Call.EVENT_HANDOVER_FAILED, null); 2230 return; 2231 } 2232 Parcelable parcelable = extras.getParcelable( 2233 android.telecom.Call.EXTRA_HANDOVER_PHONE_ACCOUNT_HANDLE); 2234 if (!(parcelable instanceof PhoneAccountHandle) || parcelable == null) { 2235 Log.w(this, "sendCallEvent: %s event received with invalid handover acct.", 2236 android.telecom.Call.EVENT_REQUEST_HANDOVER); 2237 mConnectionService.sendCallEvent(this, 2238 android.telecom.Call.EVENT_HANDOVER_FAILED, null); 2239 return; 2240 } 2241 PhoneAccountHandle phoneAccountHandle = (PhoneAccountHandle) parcelable; 2242 int videoState = extras.getInt(android.telecom.Call.EXTRA_HANDOVER_VIDEO_STATE, 2243 VideoProfile.STATE_AUDIO_ONLY); 2244 Parcelable handoverExtras = extras.getParcelable( 2245 android.telecom.Call.EXTRA_HANDOVER_EXTRAS); 2246 Bundle handoverExtrasBundle = null; 2247 if (handoverExtras instanceof Bundle) { 2248 handoverExtrasBundle = (Bundle) handoverExtras; 2249 } 2250 requestHandover(phoneAccountHandle, videoState, handoverExtrasBundle, true); 2251 } else { 2252 Log.addEvent(this, LogUtils.Events.CALL_EVENT, event); 2253 mConnectionService.sendCallEvent(this, event, extras); 2254 } 2255 } else { 2256 Log.e(this, new NullPointerException(), 2257 "sendCallEvent failed due to null CS callId=%s", getId()); 2258 } 2259 } 2260 2261 /** 2262 * Initiates a handover of this Call to the {@link ConnectionService} identified 2263 * by destAcct. 2264 * @param destAcct ConnectionService to which the call should be handed over. 2265 * @param videoState The video state desired after the handover. 2266 * @param extras Extra information to be passed to ConnectionService 2267 */ 2268 public void handoverTo(PhoneAccountHandle destAcct, int videoState, Bundle extras) { 2269 requestHandover(destAcct, videoState, extras, false); 2270 } 2271 2272 /** 2273 * Sets this {@link Call} to has the specified {@code parentCall}. Also sets the parent to 2274 * have this call as a child. 2275 * @param parentCall 2276 */ 2277 void setParentAndChildCall(Call parentCall) { 2278 boolean isParentChanging = (mParentCall != parentCall); 2279 setParentCall(parentCall); 2280 setChildOf(parentCall); 2281 if (isParentChanging) { 2282 notifyParentChanged(parentCall); 2283 } 2284 } 2285 2286 /** 2287 * Notifies listeners when the parent call changes. 2288 * Used by {@link #setParentAndChildCall(Call)}, and in {@link CallsManager}. 2289 * @param parentCall The new parent call for this call. 2290 */ 2291 void notifyParentChanged(Call parentCall) { 2292 Log.addEvent(this, LogUtils.Events.SET_PARENT, parentCall); 2293 for (Listener l : mListeners) { 2294 l.onParentChanged(this); 2295 } 2296 } 2297 2298 /** 2299 * Unlike {@link #setParentAndChildCall(Call)}, only sets the parent call but does NOT set 2300 * the child. 2301 * TODO: This is only required when adding existing connections as a workaround so that we 2302 * can avoid sending the "onParentChanged" callback until later. 2303 * @param parentCall The new parent call. 2304 */ 2305 void setParentCall(Call parentCall) { 2306 if (parentCall == this) { 2307 Log.e(this, new Exception(), "setting the parent to self"); 2308 return; 2309 } 2310 if (parentCall == mParentCall) { 2311 // nothing to do 2312 return; 2313 } 2314 if (mParentCall != null) { 2315 mParentCall.removeChildCall(this); 2316 } 2317 mParentCall = parentCall; 2318 } 2319 2320 /** 2321 * To be called after {@link #setParentCall(Call)} to complete setting the parent by adding 2322 * this call as a child of another call. 2323 * <p> 2324 * Note: if using this method alone, the caller must call {@link #notifyParentChanged(Call)} to 2325 * ensure the InCall UI is updated with the change in parent. 2326 * @param parentCall The new parent for this call. 2327 */ 2328 void setChildOf(Call parentCall) { 2329 if (parentCall != null && !parentCall.getChildCalls().contains(this)) { 2330 parentCall.addChildCall(this); 2331 } 2332 } 2333 2334 void setConferenceableCalls(List<Call> conferenceableCalls) { 2335 mConferenceableCalls.clear(); 2336 mConferenceableCalls.addAll(conferenceableCalls); 2337 2338 for (Listener l : mListeners) { 2339 l.onConferenceableCallsChanged(this); 2340 } 2341 } 2342 2343 @VisibleForTesting 2344 public List<Call> getConferenceableCalls() { 2345 return mConferenceableCalls; 2346 } 2347 2348 @VisibleForTesting 2349 public boolean can(int capability) { 2350 return (mConnectionCapabilities & capability) == capability; 2351 } 2352 2353 @VisibleForTesting 2354 public boolean hasProperty(int property) { 2355 return (mConnectionProperties & property) == property; 2356 } 2357 2358 private void addChildCall(Call call) { 2359 if (!mChildCalls.contains(call)) { 2360 // Set the pseudo-active call to the latest child added to the conference. 2361 // See definition of mConferenceLevelActiveCall for more detail. 2362 mConferenceLevelActiveCall = call; 2363 mChildCalls.add(call); 2364 2365 Log.addEvent(this, LogUtils.Events.ADD_CHILD, call); 2366 2367 for (Listener l : mListeners) { 2368 l.onChildrenChanged(this); 2369 } 2370 } 2371 } 2372 2373 private void removeChildCall(Call call) { 2374 if (mChildCalls.remove(call)) { 2375 Log.addEvent(this, LogUtils.Events.REMOVE_CHILD, call); 2376 for (Listener l : mListeners) { 2377 l.onChildrenChanged(this); 2378 } 2379 } 2380 } 2381 2382 /** 2383 * Return whether the user can respond to this {@code Call} via an SMS message. 2384 * 2385 * @return true if the "Respond via SMS" feature should be enabled 2386 * for this incoming call. 2387 * 2388 * The general rule is that we *do* allow "Respond via SMS" except for 2389 * the few (relatively rare) cases where we know for sure it won't 2390 * work, namely: 2391 * - a bogus or blank incoming number 2392 * - a call from a SIP address 2393 * - a "call presentation" that doesn't allow the number to be revealed 2394 * 2395 * In all other cases, we allow the user to respond via SMS. 2396 * 2397 * Note that this behavior isn't perfect; for example we have no way 2398 * to detect whether the incoming call is from a landline (with most 2399 * networks at least), so we still enable this feature even though 2400 * SMSes to that number will silently fail. 2401 */ 2402 boolean isRespondViaSmsCapable() { 2403 if (mState != CallState.RINGING) { 2404 return false; 2405 } 2406 2407 if (getHandle() == null) { 2408 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in 2409 // other words, the user should not be able to see the incoming phone number. 2410 return false; 2411 } 2412 2413 if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) { 2414 // The incoming number is actually a URI (i.e. a SIP address), 2415 // not a regular PSTN phone number, and we can't send SMSes to 2416 // SIP addresses. 2417 // (TODO: That might still be possible eventually, though. Is 2418 // there some SIP-specific equivalent to sending a text message?) 2419 return false; 2420 } 2421 2422 // Is there a valid SMS application on the phone? 2423 if (SmsApplication.getDefaultRespondViaMessageApplication(mContext, 2424 true /*updateIfNeeded*/) == null) { 2425 return false; 2426 } 2427 2428 // TODO: with some carriers (in certain countries) you *can* actually 2429 // tell whether a given number is a mobile phone or not. So in that 2430 // case we could potentially return false here if the incoming call is 2431 // from a land line. 2432 2433 // If none of the above special cases apply, it's OK to enable the 2434 // "Respond via SMS" feature. 2435 return true; 2436 } 2437 2438 List<String> getCannedSmsResponses() { 2439 return mCannedSmsResponses; 2440 } 2441 2442 /** 2443 * We need to make sure that before we move a call to the disconnected state, it no 2444 * longer has any parent/child relationships. We want to do this to ensure that the InCall 2445 * Service always has the right data in the right order. We also want to do it in telecom so 2446 * that the insurance policy lives in the framework side of things. 2447 */ 2448 private void fixParentAfterDisconnect() { 2449 setParentAndChildCall(null); 2450 } 2451 2452 /** 2453 * @return True if the call is ringing, else logs the action name. 2454 */ 2455 private boolean isRinging(String actionName) { 2456 if (mState == CallState.RINGING) { 2457 return true; 2458 } 2459 2460 Log.i(this, "Request to %s a non-ringing call %s", actionName, this); 2461 return false; 2462 } 2463 2464 @SuppressWarnings("rawtypes") 2465 private void decrementAssociatedCallCount(ServiceBinder binder) { 2466 if (binder != null) { 2467 binder.decrementAssociatedCallCount(); 2468 } 2469 } 2470 2471 /** 2472 * Looks up contact information based on the current handle. 2473 */ 2474 private void startCallerInfoLookup() { 2475 mCallerInfo = null; 2476 mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener); 2477 } 2478 2479 /** 2480 * Saves the specified caller info if the specified token matches that of the last query 2481 * that was made. 2482 * 2483 * @param callerInfo The new caller information to set. 2484 */ 2485 private void setCallerInfo(Uri handle, CallerInfo callerInfo) { 2486 Trace.beginSection("setCallerInfo"); 2487 if (callerInfo == null) { 2488 Log.i(this, "CallerInfo lookup returned null, skipping update"); 2489 return; 2490 } 2491 2492 if ((handle != null) && !handle.equals(mHandle)) { 2493 Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring."); 2494 return; 2495 } 2496 2497 mCallerInfo = callerInfo; 2498 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo); 2499 2500 if (mCallerInfo.contactDisplayPhotoUri == null || 2501 mCallerInfo.cachedPhotoIcon != null || mCallerInfo.cachedPhoto != null) { 2502 for (Listener l : mListeners) { 2503 l.onCallerInfoChanged(this); 2504 } 2505 } 2506 2507 Trace.endSection(); 2508 } 2509 2510 public CallerInfo getCallerInfo() { 2511 return mCallerInfo; 2512 } 2513 2514 private void maybeLoadCannedSmsResponses() { 2515 if (mCallDirection == CALL_DIRECTION_INCOMING 2516 && isRespondViaSmsCapable() 2517 && !mCannedSmsResponsesLoadingStarted) { 2518 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages"); 2519 mCannedSmsResponsesLoadingStarted = true; 2520 mCallsManager.getRespondViaSmsManager().loadCannedTextMessages( 2521 new Response<Void, List<String>>() { 2522 @Override 2523 public void onResult(Void request, List<String>... result) { 2524 if (result.length > 0) { 2525 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]); 2526 mCannedSmsResponses = result[0]; 2527 for (Listener l : mListeners) { 2528 l.onCannedSmsResponsesLoaded(Call.this); 2529 } 2530 } 2531 } 2532 2533 @Override 2534 public void onError(Void request, int code, String msg) { 2535 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code, 2536 msg); 2537 } 2538 }, 2539 mContext 2540 ); 2541 } else { 2542 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing"); 2543 } 2544 } 2545 2546 /** 2547 * Sets speakerphone option on when call begins. 2548 */ 2549 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) { 2550 mSpeakerphoneOn = startWithSpeakerphone; 2551 } 2552 2553 /** 2554 * Returns speakerphone option. 2555 * 2556 * @return Whether or not speakerphone should be set automatically when call begins. 2557 */ 2558 public boolean getStartWithSpeakerphoneOn() { 2559 return mSpeakerphoneOn; 2560 } 2561 2562 public void setRequestedToStartWithRtt() { 2563 mDidRequestToStartWithRtt = true; 2564 } 2565 2566 public void stopRtt() { 2567 if (mConnectionService != null) { 2568 mConnectionService.stopRtt(this); 2569 } else { 2570 // If this gets called by the in-call app before the connection service is set, we'll 2571 // just ignore it since it's really not supposed to happen. 2572 Log.w(this, "stopRtt() called before connection service is set."); 2573 } 2574 } 2575 2576 public void sendRttRequest() { 2577 createRttStreams(); 2578 mConnectionService.startRtt(this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs()); 2579 } 2580 2581 private boolean areRttStreamsInitialized() { 2582 return mInCallToConnectionServiceStreams != null 2583 && mConnectionServiceToInCallStreams != null; 2584 } 2585 2586 public void createRttStreams() { 2587 if (!areRttStreamsInitialized()) { 2588 Log.i(this, "Initializing RTT streams"); 2589 try { 2590 mWasEverRtt = true; 2591 mInCallToConnectionServiceStreams = ParcelFileDescriptor.createReliablePipe(); 2592 mConnectionServiceToInCallStreams = ParcelFileDescriptor.createReliablePipe(); 2593 } catch (IOException e) { 2594 Log.e(this, e, "Failed to create pipes for RTT call."); 2595 } 2596 } 2597 } 2598 2599 public void onRttConnectionFailure(int reason) { 2600 Log.i(this, "Got RTT initiation failure with reason %d", reason); 2601 for (Listener l : mListeners) { 2602 l.onRttInitiationFailure(this, reason); 2603 } 2604 } 2605 2606 public void onRemoteRttRequest() { 2607 if (isRttCall()) { 2608 Log.w(this, "Remote RTT request on a call that's already RTT"); 2609 return; 2610 } 2611 2612 mPendingRttRequestId = mCallsManager.getNextRttRequestId(); 2613 for (Listener l : mListeners) { 2614 l.onRemoteRttRequest(this, mPendingRttRequestId); 2615 } 2616 } 2617 2618 public void handleRttRequestResponse(int id, boolean accept) { 2619 if (mPendingRttRequestId == INVALID_RTT_REQUEST_ID) { 2620 Log.w(this, "Response received to a nonexistent RTT request: %d", id); 2621 return; 2622 } 2623 if (id != mPendingRttRequestId) { 2624 Log.w(this, "Response ID %d does not match expected %d", id, mPendingRttRequestId); 2625 return; 2626 } 2627 if (accept) { 2628 createRttStreams(); 2629 Log.i(this, "RTT request %d accepted.", id); 2630 mConnectionService.respondToRttRequest( 2631 this, getInCallToCsRttPipeForCs(), getCsToInCallRttPipeForCs()); 2632 } else { 2633 Log.i(this, "RTT request %d rejected.", id); 2634 mConnectionService.respondToRttRequest(this, null, null); 2635 } 2636 } 2637 2638 public boolean isRttCall() { 2639 return (mConnectionProperties & Connection.PROPERTY_IS_RTT) == Connection.PROPERTY_IS_RTT; 2640 } 2641 2642 public boolean wasEverRttCall() { 2643 return mWasEverRtt; 2644 } 2645 2646 public ParcelFileDescriptor getCsToInCallRttPipeForCs() { 2647 return mConnectionServiceToInCallStreams == null ? null 2648 : mConnectionServiceToInCallStreams[RTT_PIPE_WRITE_SIDE_INDEX]; 2649 } 2650 2651 public ParcelFileDescriptor getInCallToCsRttPipeForCs() { 2652 return mInCallToConnectionServiceStreams == null ? null 2653 : mInCallToConnectionServiceStreams[RTT_PIPE_READ_SIDE_INDEX]; 2654 } 2655 2656 public ParcelFileDescriptor getCsToInCallRttPipeForInCall() { 2657 return mConnectionServiceToInCallStreams == null ? null 2658 : mConnectionServiceToInCallStreams[RTT_PIPE_READ_SIDE_INDEX]; 2659 } 2660 2661 public ParcelFileDescriptor getInCallToCsRttPipeForInCall() { 2662 return mInCallToConnectionServiceStreams == null ? null 2663 : mInCallToConnectionServiceStreams[RTT_PIPE_WRITE_SIDE_INDEX]; 2664 } 2665 2666 public int getRttMode() { 2667 return mRttMode; 2668 } 2669 2670 /** 2671 * Sets a video call provider for the call. 2672 */ 2673 public void setVideoProvider(IVideoProvider videoProvider) { 2674 Log.v(this, "setVideoProvider"); 2675 2676 if (mVideoProviderProxy != null) { 2677 mVideoProviderProxy.clearVideoCallback(); 2678 mVideoProviderProxy = null; 2679 } 2680 2681 if (videoProvider != null ) { 2682 try { 2683 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this, 2684 mCallsManager); 2685 } catch (RemoteException ignored) { 2686 // Ignore RemoteException. 2687 } 2688 } 2689 2690 mVideoProvider = videoProvider; 2691 2692 for (Listener l : mListeners) { 2693 l.onVideoCallProviderChanged(Call.this); 2694 } 2695 } 2696 2697 /** 2698 * @return The {@link Connection.VideoProvider} binder. 2699 */ 2700 public IVideoProvider getVideoProvider() { 2701 if (mVideoProviderProxy == null) { 2702 return null; 2703 } 2704 2705 return mVideoProviderProxy.getInterface(); 2706 } 2707 2708 /** 2709 * @return The {@link VideoProviderProxy} for this call. 2710 */ 2711 public VideoProviderProxy getVideoProviderProxy() { 2712 return mVideoProviderProxy; 2713 } 2714 2715 /** 2716 * The current video state for the call. 2717 * See {@link VideoProfile} for a list of valid video states. 2718 */ 2719 public int getVideoState() { 2720 return mVideoState; 2721 } 2722 2723 /** 2724 * Returns the video states which were applicable over the duration of a call. 2725 * See {@link VideoProfile} for a list of valid video states. 2726 * 2727 * @return The video states applicable over the duration of the call. 2728 */ 2729 public int getVideoStateHistory() { 2730 return mVideoStateHistory; 2731 } 2732 2733 /** 2734 * Determines the current video state for the call. 2735 * For an outgoing call determines the desired video state for the call. 2736 * Valid values: see {@link VideoProfile} 2737 * 2738 * @param videoState The video state for the call. 2739 */ 2740 public void setVideoState(int videoState) { 2741 // If the phone account associated with this call does not support video calling, then we 2742 // will automatically set the video state to audio-only. 2743 if (!isVideoCallingSupported()) { 2744 Log.d(this, "setVideoState: videoState=%s defaulted to audio (video not supported)", 2745 VideoProfile.videoStateToString(videoState)); 2746 videoState = VideoProfile.STATE_AUDIO_ONLY; 2747 } 2748 2749 // Track Video State history during the duration of the call. 2750 // Only update the history when the call is active or disconnected. This ensures we do 2751 // not include the video state history when: 2752 // - Call is incoming (but not answered). 2753 // - Call it outgoing (but not answered). 2754 // We include the video state when disconnected to ensure that rejected calls reflect the 2755 // appropriate video state. 2756 // For all other times we add to the video state history, see #setState. 2757 if (isActive() || getState() == CallState.DISCONNECTED) { 2758 mVideoStateHistory = mVideoStateHistory | videoState; 2759 } 2760 2761 int previousVideoState = mVideoState; 2762 mVideoState = videoState; 2763 if (mVideoState != previousVideoState) { 2764 Log.addEvent(this, LogUtils.Events.VIDEO_STATE_CHANGED, 2765 VideoProfile.videoStateToString(videoState)); 2766 for (Listener l : mListeners) { 2767 l.onVideoStateChanged(this, previousVideoState, mVideoState); 2768 } 2769 } 2770 2771 if (VideoProfile.isVideo(videoState)) { 2772 mAnalytics.setCallIsVideo(true); 2773 } 2774 } 2775 2776 public boolean getIsVoipAudioMode() { 2777 return mIsVoipAudioMode; 2778 } 2779 2780 public void setIsVoipAudioMode(boolean audioModeIsVoip) { 2781 mIsVoipAudioMode = audioModeIsVoip; 2782 for (Listener l : mListeners) { 2783 l.onIsVoipAudioModeChanged(this); 2784 } 2785 } 2786 2787 public StatusHints getStatusHints() { 2788 return mStatusHints; 2789 } 2790 2791 public void setStatusHints(StatusHints statusHints) { 2792 mStatusHints = statusHints; 2793 for (Listener l : mListeners) { 2794 l.onStatusHintsChanged(this); 2795 } 2796 } 2797 2798 public boolean isUnknown() { 2799 return mCallDirection == CALL_DIRECTION_UNKNOWN; 2800 } 2801 2802 /** 2803 * Determines if this call is in a disconnecting state. 2804 * 2805 * @return {@code true} if this call is locally disconnecting. 2806 */ 2807 public boolean isLocallyDisconnecting() { 2808 return mIsLocallyDisconnecting; 2809 } 2810 2811 /** 2812 * Sets whether this call is in a disconnecting state. 2813 * 2814 * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting. 2815 */ 2816 private void setLocallyDisconnecting(boolean isLocallyDisconnecting) { 2817 mIsLocallyDisconnecting = isLocallyDisconnecting; 2818 } 2819 2820 /** 2821 * @return user handle of user initiating the outgoing call. 2822 */ 2823 public UserHandle getInitiatingUser() { 2824 return mInitiatingUser; 2825 } 2826 2827 /** 2828 * Set the user handle of user initiating the outgoing call. 2829 * @param initiatingUser 2830 */ 2831 public void setInitiatingUser(UserHandle initiatingUser) { 2832 Preconditions.checkNotNull(initiatingUser); 2833 mInitiatingUser = initiatingUser; 2834 } 2835 2836 static int getStateFromConnectionState(int state) { 2837 switch (state) { 2838 case Connection.STATE_INITIALIZING: 2839 return CallState.CONNECTING; 2840 case Connection.STATE_ACTIVE: 2841 return CallState.ACTIVE; 2842 case Connection.STATE_DIALING: 2843 return CallState.DIALING; 2844 case Connection.STATE_PULLING_CALL: 2845 return CallState.PULLING; 2846 case Connection.STATE_DISCONNECTED: 2847 return CallState.DISCONNECTED; 2848 case Connection.STATE_HOLDING: 2849 return CallState.ON_HOLD; 2850 case Connection.STATE_NEW: 2851 return CallState.NEW; 2852 case Connection.STATE_RINGING: 2853 return CallState.RINGING; 2854 } 2855 return CallState.DISCONNECTED; 2856 } 2857 2858 /** 2859 * Determines if this call is in disconnected state and waiting to be destroyed. 2860 * 2861 * @return {@code true} if this call is disconected. 2862 */ 2863 public boolean isDisconnected() { 2864 return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED); 2865 } 2866 2867 /** 2868 * Determines if this call has just been created and has not been configured properly yet. 2869 * 2870 * @return {@code true} if this call is new. 2871 */ 2872 public boolean isNew() { 2873 return getState() == CallState.NEW; 2874 } 2875 2876 /** 2877 * Sets the call data usage for the call. 2878 * 2879 * @param callDataUsage The new call data usage (in bytes). 2880 */ 2881 public void setCallDataUsage(long callDataUsage) { 2882 mCallDataUsage = callDataUsage; 2883 } 2884 2885 /** 2886 * Returns the call data usage for the call. 2887 * 2888 * @return The call data usage (in bytes). 2889 */ 2890 public long getCallDataUsage() { 2891 return mCallDataUsage; 2892 } 2893 2894 public void setRttMode(int mode) { 2895 mRttMode = mode; 2896 // TODO: hook this up to CallAudioManager 2897 } 2898 2899 /** 2900 * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent 2901 * has come back to telecom and was processed. 2902 */ 2903 public boolean isNewOutgoingCallIntentBroadcastDone() { 2904 return mIsNewOutgoingCallIntentBroadcastDone; 2905 } 2906 2907 public void setNewOutgoingCallIntentBroadcastIsDone() { 2908 mIsNewOutgoingCallIntentBroadcastDone = true; 2909 } 2910 2911 /** 2912 * Determines if the call has been held by the remote party. 2913 * 2914 * @return {@code true} if the call is remotely held, {@code false} otherwise. 2915 */ 2916 public boolean isRemotelyHeld() { 2917 return mIsRemotelyHeld; 2918 } 2919 2920 /** 2921 * Handles Connection events received from a {@link ConnectionService}. 2922 * 2923 * @param event The event. 2924 * @param extras The extras. 2925 */ 2926 public void onConnectionEvent(String event, Bundle extras) { 2927 Log.addEvent(this, LogUtils.Events.CONNECTION_EVENT, event); 2928 if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) { 2929 mIsRemotelyHeld = true; 2930 Log.addEvent(this, LogUtils.Events.REMOTELY_HELD); 2931 // Inform listeners of the fact that a call hold tone was received. This will trigger 2932 // the CallAudioManager to play a tone via the InCallTonePlayer. 2933 for (Listener l : mListeners) { 2934 l.onHoldToneRequested(this); 2935 } 2936 } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) { 2937 mIsRemotelyHeld = false; 2938 Log.addEvent(this, LogUtils.Events.REMOTELY_UNHELD); 2939 for (Listener l : mListeners) { 2940 l.onHoldToneRequested(this); 2941 } 2942 } else { 2943 for (Listener l : mListeners) { 2944 l.onConnectionEvent(this, event, extras); 2945 } 2946 } 2947 } 2948 2949 /** 2950 * Notifies interested parties that the handover has completed. 2951 * Notifies: 2952 * 1. {@link InCallController} which communicates this to the 2953 * {@link android.telecom.InCallService} via {@link Listener#onHandoverComplete()}. 2954 * 2. {@link ConnectionServiceWrapper} which informs the {@link android.telecom.Connection} of 2955 * the successful handover. 2956 */ 2957 public void onHandoverComplete() { 2958 Log.i(this, "onHandoverComplete; callId=%s", getId()); 2959 if (mConnectionService != null) { 2960 mConnectionService.handoverComplete(this); 2961 } 2962 for (Listener l : mListeners) { 2963 l.onHandoverComplete(this); 2964 } 2965 } 2966 2967 public void onHandoverFailed(int handoverError) { 2968 Log.i(this, "onHandoverFailed; callId=%s, handoverError=%d", getId(), handoverError); 2969 for (Listener l : mListeners) { 2970 l.onHandoverFailed(this, handoverError); 2971 } 2972 } 2973 2974 public void setOriginalConnectionId(String originalConnectionId) { 2975 mOriginalConnectionId = originalConnectionId; 2976 } 2977 2978 /** 2979 * For calls added via a ConnectionManager using the 2980 * {@link android.telecom.ConnectionService#addExistingConnection(PhoneAccountHandle, 2981 * Connection)}, or {@link android.telecom.ConnectionService#addConference(Conference)} APIS, 2982 * indicates the ID of this call as it was referred to by the {@code ConnectionService} which 2983 * originally created it. 2984 * 2985 * See {@link Connection#EXTRA_ORIGINAL_CONNECTION_ID}. 2986 * @return The original connection ID. 2987 */ 2988 public String getOriginalConnectionId() { 2989 return mOriginalConnectionId; 2990 } 2991 2992 public ConnectionServiceFocusManager getConnectionServiceFocusManager() { 2993 return mCallsManager.getConnectionServiceFocusManager(); 2994 } 2995 2996 /** 2997 * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either 2998 * remotely or locally. 2999 * 3000 * @param capabilities The {@link Connection} capabilities for the call. 3001 * @return {@code true} if video is supported, {@code false} otherwise. 3002 */ 3003 private boolean doesCallSupportVideo(int capabilities) { 3004 return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 || 3005 (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0; 3006 } 3007 3008 /** 3009 * Remove any video capabilities set on a {@link Connection} capabilities bitmask. 3010 * 3011 * @param capabilities The capabilities. 3012 * @return The bitmask with video capabilities removed. 3013 */ 3014 private int removeVideoCapabilities(int capabilities) { 3015 return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL | 3016 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 3017 } 3018 3019 /** 3020 * Initiates a handover of this {@link Call} to another {@link PhoneAccount}. 3021 * @param handoverToHandle The {@link PhoneAccountHandle} to handover to. 3022 * @param videoState The video state of the call when handed over. 3023 * @param extras Optional extras {@link Bundle} provided by the initiating 3024 * {@link android.telecom.InCallService}. 3025 */ 3026 private void requestHandover(PhoneAccountHandle handoverToHandle, int videoState, 3027 Bundle extras, boolean isLegacy) { 3028 for (Listener l : mListeners) { 3029 l.onHandoverRequested(this, handoverToHandle, videoState, extras, isLegacy); 3030 } 3031 } 3032 3033 /** 3034 * Sets the video history based on the state and state transitions of the call. Always add the 3035 * current video state to the video state history during a call transition except for the 3036 * transitions DIALING->ACTIVE and RINGING->ACTIVE. In these cases, clear the history. If a 3037 * call starts dialing/ringing as a VT call and gets downgraded to audio, we need to record 3038 * the history as an audio call. 3039 */ 3040 private void updateVideoHistoryViaState(int oldState, int newState) { 3041 if ((oldState == CallState.DIALING || oldState == CallState.RINGING) 3042 && newState == CallState.ACTIVE) { 3043 mVideoStateHistory = mVideoState; 3044 } 3045 3046 mVideoStateHistory |= mVideoState; 3047 } 3048 3049 /** 3050 * Returns whether or not high definition audio was used. 3051 * 3052 * @return true if high definition audio was used during this call. 3053 */ 3054 boolean wasHighDefAudio() { 3055 return mWasHighDefAudio; 3056 } 3057} 3058