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