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