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