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