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