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