Call.java revision 3ff5eddd28eeec2fed1c833a493ba7469bfa951f
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.graphics.Bitmap; 21import android.graphics.drawable.Drawable; 22import android.net.Uri; 23import android.os.Bundle; 24import android.os.Handler; 25import android.os.Looper; 26import android.os.RemoteException; 27import android.os.Trace; 28import android.provider.ContactsContract.Contacts; 29import android.telecom.DisconnectCause; 30import android.telecom.Connection; 31import android.telecom.GatewayInfo; 32import android.telecom.ParcelableConnection; 33import android.telecom.PhoneAccount; 34import android.telecom.PhoneAccountHandle; 35import android.telecom.Response; 36import android.telecom.StatusHints; 37import android.telecom.TelecomManager; 38import android.telecom.VideoProfile; 39import android.telephony.PhoneNumberUtils; 40import android.text.TextUtils; 41import android.os.UserHandle; 42 43import com.android.internal.annotations.VisibleForTesting; 44import com.android.internal.telecom.IVideoProvider; 45import com.android.internal.telephony.CallerInfo; 46import com.android.internal.telephony.SmsApplication; 47import com.android.internal.util.Preconditions; 48 49import java.lang.String; 50import java.util.ArrayList; 51import java.util.Collections; 52import java.util.LinkedList; 53import java.util.List; 54import java.util.Locale; 55import java.util.Objects; 56import java.util.Set; 57import 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 65public 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 mConnectionCapabilities = connectionCapabilities; 1017 for (Listener l : mListeners) { 1018 l.onConnectionCapabilitiesChanged(this); 1019 } 1020 } 1021 } 1022 1023 void setConnectionProperties(int connectionProperties) { 1024 Log.v(this, "setConnectionProperties: %s", Connection.propertiesToString( 1025 connectionProperties)); 1026 if (mConnectionProperties != connectionProperties) { 1027 int previousProperties = mConnectionProperties; 1028 mConnectionProperties = connectionProperties; 1029 for (Listener l : mListeners) { 1030 l.onConnectionPropertiesChanged(this); 1031 } 1032 1033 boolean wasExternal = (previousProperties & Connection.PROPERTY_IS_EXTERNAL_CALL) 1034 == Connection.PROPERTY_IS_EXTERNAL_CALL; 1035 boolean isExternal = (connectionProperties & Connection.PROPERTY_IS_EXTERNAL_CALL) 1036 == Connection.PROPERTY_IS_EXTERNAL_CALL; 1037 if (wasExternal != isExternal) { 1038 Log.v(this, "setConnectionProperties: external call changed isExternal = %b", 1039 isExternal); 1040 1041 for (Listener l : mListeners) { 1042 l.onExternalCallChanged(this, isExternal); 1043 } 1044 1045 } 1046 } 1047 } 1048 1049 @VisibleForTesting 1050 public Call getParentCall() { 1051 return mParentCall; 1052 } 1053 1054 @VisibleForTesting 1055 public List<Call> getChildCalls() { 1056 return mChildCalls; 1057 } 1058 1059 @VisibleForTesting 1060 public boolean wasConferencePreviouslyMerged() { 1061 return mWasConferencePreviouslyMerged; 1062 } 1063 1064 @VisibleForTesting 1065 public Call getConferenceLevelActiveCall() { 1066 return mConferenceLevelActiveCall; 1067 } 1068 1069 @VisibleForTesting 1070 public ConnectionServiceWrapper getConnectionService() { 1071 return mConnectionService; 1072 } 1073 1074 /** 1075 * Retrieves the {@link Context} for the call. 1076 * 1077 * @return The {@link Context}. 1078 */ 1079 Context getContext() { 1080 return mContext; 1081 } 1082 1083 @VisibleForTesting 1084 public void setConnectionService(ConnectionServiceWrapper service) { 1085 Preconditions.checkNotNull(service); 1086 1087 clearConnectionService(); 1088 1089 service.incrementAssociatedCallCount(); 1090 mConnectionService = service; 1091 mAnalytics.setCallConnectionService(service.getComponentName().flattenToShortString()); 1092 mConnectionService.addCall(this); 1093 } 1094 1095 /** 1096 * Clears the associated connection service. 1097 */ 1098 void clearConnectionService() { 1099 if (mConnectionService != null) { 1100 ConnectionServiceWrapper serviceTemp = mConnectionService; 1101 mConnectionService = null; 1102 serviceTemp.removeCall(this); 1103 1104 // Decrementing the count can cause the service to unbind, which itself can trigger the 1105 // service-death code. Since the service death code tries to clean up any associated 1106 // calls, we need to make sure to remove that information (e.g., removeCall()) before 1107 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is 1108 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good 1109 // to do. 1110 decrementAssociatedCallCount(serviceTemp); 1111 } 1112 } 1113 1114 /** 1115 * Starts the create connection sequence. Upon completion, there should exist an active 1116 * connection through a connection service (or the call will have failed). 1117 * 1118 * @param phoneAccountRegistrar The phone account registrar. 1119 */ 1120 void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { 1121 if (mCreateConnectionProcessor != null) { 1122 Log.w(this, "mCreateConnectionProcessor in startCreateConnection is not null. This is" + 1123 " due to a race between NewOutgoingCallIntentBroadcaster and " + 1124 "phoneAccountSelected, but is harmlessly resolved by ignoring the second " + 1125 "invocation."); 1126 return; 1127 } 1128 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, 1129 phoneAccountRegistrar, mContext); 1130 mCreateConnectionProcessor.process(); 1131 } 1132 1133 @Override 1134 public void handleCreateConnectionSuccess( 1135 CallIdMapper idMapper, 1136 ParcelableConnection connection) { 1137 Log.v(this, "handleCreateConnectionSuccessful %s", connection); 1138 setTargetPhoneAccount(connection.getPhoneAccount()); 1139 setHandle(connection.getHandle(), connection.getHandlePresentation()); 1140 setCallerDisplayName( 1141 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation()); 1142 1143 setConnectionCapabilities(connection.getConnectionCapabilities()); 1144 setConnectionProperties(connection.getConnectionProperties()); 1145 setVideoProvider(connection.getVideoProvider()); 1146 setVideoState(connection.getVideoState()); 1147 setRingbackRequested(connection.isRingbackRequested()); 1148 setIsVoipAudioMode(connection.getIsVoipAudioMode()); 1149 setStatusHints(connection.getStatusHints()); 1150 putExtras(SOURCE_CONNECTION_SERVICE, connection.getExtras()); 1151 1152 mConferenceableCalls.clear(); 1153 for (String id : connection.getConferenceableConnectionIds()) { 1154 mConferenceableCalls.add(idMapper.getCall(id)); 1155 } 1156 1157 switch (mCallDirection) { 1158 case CALL_DIRECTION_INCOMING: 1159 // Listeners (just CallsManager for now) will be responsible for checking whether 1160 // the call should be blocked. 1161 for (Listener l : mListeners) { 1162 l.onSuccessfulIncomingCall(this); 1163 } 1164 break; 1165 case CALL_DIRECTION_OUTGOING: 1166 for (Listener l : mListeners) { 1167 l.onSuccessfulOutgoingCall(this, 1168 getStateFromConnectionState(connection.getState())); 1169 } 1170 break; 1171 case CALL_DIRECTION_UNKNOWN: 1172 for (Listener l : mListeners) { 1173 l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection 1174 .getState())); 1175 } 1176 break; 1177 } 1178 } 1179 1180 @Override 1181 public void handleCreateConnectionFailure(DisconnectCause disconnectCause) { 1182 clearConnectionService(); 1183 setDisconnectCause(disconnectCause); 1184 mCallsManager.markCallAsDisconnected(this, disconnectCause); 1185 1186 switch (mCallDirection) { 1187 case CALL_DIRECTION_INCOMING: 1188 for (Listener listener : mListeners) { 1189 listener.onFailedIncomingCall(this); 1190 } 1191 break; 1192 case CALL_DIRECTION_OUTGOING: 1193 for (Listener listener : mListeners) { 1194 listener.onFailedOutgoingCall(this, disconnectCause); 1195 } 1196 break; 1197 case CALL_DIRECTION_UNKNOWN: 1198 for (Listener listener : mListeners) { 1199 listener.onFailedUnknownCall(this); 1200 } 1201 break; 1202 } 1203 } 1204 1205 /** 1206 * Plays the specified DTMF tone. 1207 */ 1208 void playDtmfTone(char digit) { 1209 if (mConnectionService == null) { 1210 Log.w(this, "playDtmfTone() request on a call without a connection service."); 1211 } else { 1212 Log.i(this, "Send playDtmfTone to connection service for call %s", this); 1213 mConnectionService.playDtmfTone(this, digit); 1214 Log.event(this, Log.Events.START_DTMF, Log.pii(digit)); 1215 } 1216 } 1217 1218 /** 1219 * Stops playing any currently playing DTMF tone. 1220 */ 1221 void stopDtmfTone() { 1222 if (mConnectionService == null) { 1223 Log.w(this, "stopDtmfTone() request on a call without a connection service."); 1224 } else { 1225 Log.i(this, "Send stopDtmfTone to connection service for call %s", this); 1226 Log.event(this, Log.Events.STOP_DTMF); 1227 mConnectionService.stopDtmfTone(this); 1228 } 1229 } 1230 1231 /** 1232 * Silences the ringer. 1233 */ 1234 void silence() { 1235 if (mConnectionService == null) { 1236 Log.w(this, "silence() request on a call without a connection service."); 1237 } else { 1238 Log.i(this, "Send silence to connection service for call %s", this); 1239 Log.event(this, Log.Events.SILENCE); 1240 mConnectionService.silence(this); 1241 } 1242 } 1243 1244 @VisibleForTesting 1245 public void disconnect() { 1246 disconnect(false); 1247 } 1248 1249 /** 1250 * Attempts to disconnect the call through the connection service. 1251 */ 1252 @VisibleForTesting 1253 public void disconnect(boolean wasViaNewOutgoingCallBroadcaster) { 1254 Log.event(this, Log.Events.REQUEST_DISCONNECT); 1255 1256 // Track that the call is now locally disconnecting. 1257 setLocallyDisconnecting(true); 1258 1259 if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT || 1260 mState == CallState.CONNECTING) { 1261 Log.v(this, "Aborting call %s", this); 1262 abort(wasViaNewOutgoingCallBroadcaster); 1263 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) { 1264 if (mConnectionService == null) { 1265 Log.e(this, new Exception(), "disconnect() request on a call without a" 1266 + " connection service."); 1267 } else { 1268 Log.i(this, "Send disconnect to connection service for call: %s", this); 1269 // The call isn't officially disconnected until the connection service 1270 // confirms that the call was actually disconnected. Only then is the 1271 // association between call and connection service severed, see 1272 // {@link CallsManager#markCallAsDisconnected}. 1273 mConnectionService.disconnect(this); 1274 } 1275 } 1276 } 1277 1278 void abort(boolean wasViaNewOutgoingCallBroadcaster) { 1279 if (mCreateConnectionProcessor != null && 1280 !mCreateConnectionProcessor.isProcessingComplete()) { 1281 mCreateConnectionProcessor.abort(); 1282 } else if (mState == CallState.NEW || mState == CallState.SELECT_PHONE_ACCOUNT 1283 || mState == CallState.CONNECTING) { 1284 if (wasViaNewOutgoingCallBroadcaster) { 1285 // If the cancelation was from NEW_OUTGOING_CALL, then we do not automatically 1286 // destroy the call. Instead, we announce the cancelation and CallsManager handles 1287 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and 1288 // then re-dial them quickly using a gateway, allowing the first call to end 1289 // causes jank. This timeout allows CallsManager to transition the first call into 1290 // the second call so that in-call only ever sees a single call...eliminating the 1291 // jank altogether. 1292 for (Listener listener : mListeners) { 1293 if (listener.onCanceledViaNewOutgoingCallBroadcast(this)) { 1294 // The first listener to handle this wins. A return value of true means that 1295 // the listener will handle the disconnection process later and so we 1296 // should not continue it here. 1297 setLocallyDisconnecting(false); 1298 return; 1299 } 1300 } 1301 } 1302 1303 handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED)); 1304 } else { 1305 Log.v(this, "Cannot abort a call which is neither SELECT_PHONE_ACCOUNT or CONNECTING"); 1306 } 1307 } 1308 1309 /** 1310 * Answers the call if it is ringing. 1311 * 1312 * @param videoState The video state in which to answer the call. 1313 */ 1314 @VisibleForTesting 1315 public void answer(int videoState) { 1316 Preconditions.checkNotNull(mConnectionService); 1317 1318 // Check to verify that the call is still in the ringing state. A call can change states 1319 // between the time the user hits 'answer' and Telecom receives the command. 1320 if (isRinging("answer")) { 1321 // At this point, we are asking the connection service to answer but we don't assume 1322 // that it will work. Instead, we wait until confirmation from the connectino service 1323 // that the call is in a non-STATE_RINGING state before changing the UI. See 1324 // {@link ConnectionServiceAdapter#setActive} and other set* methods. 1325 mConnectionService.answer(this, videoState); 1326 Log.event(this, Log.Events.REQUEST_ACCEPT); 1327 } 1328 } 1329 1330 /** 1331 * Rejects the call if it is ringing. 1332 * 1333 * @param rejectWithMessage Whether to send a text message as part of the call rejection. 1334 * @param textMessage An optional text message to send as part of the rejection. 1335 */ 1336 @VisibleForTesting 1337 public void reject(boolean rejectWithMessage, String textMessage) { 1338 Preconditions.checkNotNull(mConnectionService); 1339 1340 // Check to verify that the call is still in the ringing state. A call can change states 1341 // between the time the user hits 'reject' and Telecomm receives the command. 1342 if (isRinging("reject")) { 1343 // Ensure video state history tracks video state at time of rejection. 1344 mVideoStateHistory |= mVideoState; 1345 1346 mConnectionService.reject(this, rejectWithMessage, textMessage); 1347 Log.event(this, Log.Events.REQUEST_REJECT); 1348 } 1349 } 1350 1351 /** 1352 * Puts the call on hold if it is currently active. 1353 */ 1354 void hold() { 1355 Preconditions.checkNotNull(mConnectionService); 1356 1357 if (mState == CallState.ACTIVE) { 1358 mConnectionService.hold(this); 1359 Log.event(this, Log.Events.REQUEST_HOLD); 1360 } 1361 } 1362 1363 /** 1364 * Releases the call from hold if it is currently active. 1365 */ 1366 void unhold() { 1367 Preconditions.checkNotNull(mConnectionService); 1368 1369 if (mState == CallState.ON_HOLD) { 1370 mConnectionService.unhold(this); 1371 Log.event(this, Log.Events.REQUEST_UNHOLD); 1372 } 1373 } 1374 1375 /** Checks if this is a live call or not. */ 1376 @VisibleForTesting 1377 public boolean isAlive() { 1378 switch (mState) { 1379 case CallState.NEW: 1380 case CallState.RINGING: 1381 case CallState.DISCONNECTED: 1382 case CallState.ABORTED: 1383 return false; 1384 default: 1385 return true; 1386 } 1387 } 1388 1389 boolean isActive() { 1390 return mState == CallState.ACTIVE; 1391 } 1392 1393 Bundle getExtras() { 1394 return mExtras; 1395 } 1396 1397 /** 1398 * Adds extras to the extras bundle associated with this {@link Call}. 1399 * 1400 * Note: this method needs to know the source of the extras change (see 1401 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which 1402 * originate from a connection service will only be notified to incall services. Likewise, 1403 * changes originating from the incall services will only notify the connection service of the 1404 * change. 1405 * 1406 * @param source The source of the extras addition. 1407 * @param extras The extras. 1408 */ 1409 void putExtras(int source, Bundle extras) { 1410 if (extras == null) { 1411 return; 1412 } 1413 if (mExtras == null) { 1414 mExtras = new Bundle(); 1415 } 1416 mExtras.putAll(extras); 1417 1418 for (Listener l : mListeners) { 1419 l.onExtrasChanged(this, source, extras); 1420 } 1421 1422 // If the change originated from an InCallService, notify the connection service. 1423 if (source == SOURCE_INCALL_SERVICE) { 1424 mConnectionService.onExtrasChanged(this, mExtras); 1425 } 1426 } 1427 1428 /** 1429 * Removes extras from the extras bundle associated with this {@link Call}. 1430 * 1431 * Note: this method needs to know the source of the extras change (see 1432 * {@link #SOURCE_CONNECTION_SERVICE}, {@link #SOURCE_INCALL_SERVICE}). Extras changes which 1433 * originate from a connection service will only be notified to incall services. Likewise, 1434 * changes originating from the incall services will only notify the connection service of the 1435 * change. 1436 * 1437 * @param source The source of the extras removal. 1438 * @param keys The extra keys to remove. 1439 */ 1440 void removeExtras(int source, List<String> keys) { 1441 if (mExtras == null) { 1442 return; 1443 } 1444 for (String key : keys) { 1445 mExtras.remove(key); 1446 } 1447 1448 for (Listener l : mListeners) { 1449 l.onExtrasRemoved(this, source, keys); 1450 } 1451 1452 // If the change originated from an InCallService, notify the connection service. 1453 if (source == SOURCE_INCALL_SERVICE) { 1454 mConnectionService.onExtrasChanged(this, mExtras); 1455 } 1456 } 1457 1458 @VisibleForTesting 1459 public Bundle getIntentExtras() { 1460 return mIntentExtras; 1461 } 1462 1463 void setIntentExtras(Bundle extras) { 1464 mIntentExtras = extras; 1465 } 1466 1467 /** 1468 * @return the uri of the contact associated with this call. 1469 */ 1470 @VisibleForTesting 1471 public Uri getContactUri() { 1472 if (mCallerInfo == null || !mCallerInfo.contactExists) { 1473 return getHandle(); 1474 } 1475 return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey); 1476 } 1477 1478 Uri getRingtone() { 1479 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri; 1480 } 1481 1482 void onPostDialWait(String remaining) { 1483 for (Listener l : mListeners) { 1484 l.onPostDialWait(this, remaining); 1485 } 1486 } 1487 1488 void onPostDialChar(char nextChar) { 1489 for (Listener l : mListeners) { 1490 l.onPostDialChar(this, nextChar); 1491 } 1492 } 1493 1494 void postDialContinue(boolean proceed) { 1495 mConnectionService.onPostDialContinue(this, proceed); 1496 } 1497 1498 void conferenceWith(Call otherCall) { 1499 if (mConnectionService == null) { 1500 Log.w(this, "conference requested on a call without a connection service."); 1501 } else { 1502 Log.event(this, Log.Events.CONFERENCE_WITH, otherCall); 1503 mConnectionService.conference(this, otherCall); 1504 } 1505 } 1506 1507 void splitFromConference() { 1508 if (mConnectionService == null) { 1509 Log.w(this, "splitting from conference call without a connection service"); 1510 } else { 1511 Log.event(this, Log.Events.SPLIT_FROM_CONFERENCE); 1512 mConnectionService.splitFromConference(this); 1513 } 1514 } 1515 1516 @VisibleForTesting 1517 public void mergeConference() { 1518 if (mConnectionService == null) { 1519 Log.w(this, "merging conference calls without a connection service."); 1520 } else if (can(Connection.CAPABILITY_MERGE_CONFERENCE)) { 1521 Log.event(this, Log.Events.CONFERENCE_WITH); 1522 mConnectionService.mergeConference(this); 1523 mWasConferencePreviouslyMerged = true; 1524 } 1525 } 1526 1527 @VisibleForTesting 1528 public void swapConference() { 1529 if (mConnectionService == null) { 1530 Log.w(this, "swapping conference calls without a connection service."); 1531 } else if (can(Connection.CAPABILITY_SWAP_CONFERENCE)) { 1532 Log.event(this, Log.Events.SWAP); 1533 mConnectionService.swapConference(this); 1534 switch (mChildCalls.size()) { 1535 case 1: 1536 mConferenceLevelActiveCall = mChildCalls.get(0); 1537 break; 1538 case 2: 1539 // swap 1540 mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ? 1541 mChildCalls.get(1) : mChildCalls.get(0); 1542 break; 1543 default: 1544 // For anything else 0, or 3+, set it to null since it is impossible to tell. 1545 mConferenceLevelActiveCall = null; 1546 break; 1547 } 1548 } 1549 } 1550 1551 /** 1552 * Initiates a request to the connection service to pull this call. 1553 * <p> 1554 * This method can only be used for calls that have the 1555 * {@link android.telecom.Connection#CAPABILITY_CAN_PULL_CALL} capability and 1556 * {@link android.telecom.Connection#PROPERTY_IS_EXTERNAL_CALL} property set. 1557 * <p> 1558 * An external call is a representation of a call which is taking place on another device 1559 * associated with a PhoneAccount on this device. Issuing a request to pull the external call 1560 * tells the {@link android.telecom.ConnectionService} that it should move the call from the 1561 * other device to this one. An example of this is the IMS multi-endpoint functionality. A 1562 * user may have two phones with the same phone number. If the user is engaged in an active 1563 * call on their first device, the network will inform the second device of that ongoing call in 1564 * the form of an external call. The user may wish to continue their conversation on the second 1565 * device, so will issue a request to pull the call to the second device. 1566 * <p> 1567 * Requests to pull a call which is not external, or a call which is not pullable are ignored. 1568 */ 1569 public void pullExternalCall() { 1570 if (mConnectionService == null) { 1571 Log.w(this, "pulling a call without a connection service."); 1572 } 1573 1574 if (!hasProperty(Connection.PROPERTY_IS_EXTERNAL_CALL)) { 1575 Log.w(this, "pullExternalCall - call %s is not an external call.", mId); 1576 return; 1577 } 1578 1579 if (!can(Connection.CAPABILITY_CAN_PULL_CALL)) { 1580 Log.w(this, "pullExternalCall - call %s is external but cannot be pulled.", mId); 1581 return; 1582 } 1583 1584 Log.event(this, Log.Events.REQUEST_PULL); 1585 mConnectionService.pullExternalCall(this); 1586 } 1587 1588 /** 1589 * Sends a call event to the {@link ConnectionService} for this call. 1590 * 1591 * See {@link Call#sendCallEvent(String, Bundle)}. 1592 * 1593 * @param event The call event. 1594 * @param extras Associated extras. 1595 */ 1596 public void sendCallEvent(String event, Bundle extras) { 1597 mConnectionService.sendCallEvent(this, event, extras); 1598 } 1599 1600 void setParentCall(Call parentCall) { 1601 if (parentCall == this) { 1602 Log.e(this, new Exception(), "setting the parent to self"); 1603 return; 1604 } 1605 if (parentCall == mParentCall) { 1606 // nothing to do 1607 return; 1608 } 1609 Preconditions.checkState(parentCall == null || mParentCall == null); 1610 1611 Call oldParent = mParentCall; 1612 if (mParentCall != null) { 1613 mParentCall.removeChildCall(this); 1614 } 1615 mParentCall = parentCall; 1616 if (mParentCall != null) { 1617 mParentCall.addChildCall(this); 1618 } 1619 1620 Log.event(this, Log.Events.SET_PARENT, mParentCall); 1621 for (Listener l : mListeners) { 1622 l.onParentChanged(this); 1623 } 1624 } 1625 1626 void setConferenceableCalls(List<Call> conferenceableCalls) { 1627 mConferenceableCalls.clear(); 1628 mConferenceableCalls.addAll(conferenceableCalls); 1629 1630 for (Listener l : mListeners) { 1631 l.onConferenceableCallsChanged(this); 1632 } 1633 } 1634 1635 @VisibleForTesting 1636 public List<Call> getConferenceableCalls() { 1637 return mConferenceableCalls; 1638 } 1639 1640 @VisibleForTesting 1641 public boolean can(int capability) { 1642 return (mConnectionCapabilities & capability) == capability; 1643 } 1644 1645 @VisibleForTesting 1646 public boolean hasProperty(int property) { 1647 return (mConnectionProperties & property) == property; 1648 } 1649 1650 private void addChildCall(Call call) { 1651 if (!mChildCalls.contains(call)) { 1652 // Set the pseudo-active call to the latest child added to the conference. 1653 // See definition of mConferenceLevelActiveCall for more detail. 1654 mConferenceLevelActiveCall = call; 1655 mChildCalls.add(call); 1656 1657 Log.event(this, Log.Events.ADD_CHILD, call); 1658 1659 for (Listener l : mListeners) { 1660 l.onChildrenChanged(this); 1661 } 1662 } 1663 } 1664 1665 private void removeChildCall(Call call) { 1666 if (mChildCalls.remove(call)) { 1667 Log.event(this, Log.Events.REMOVE_CHILD, call); 1668 for (Listener l : mListeners) { 1669 l.onChildrenChanged(this); 1670 } 1671 } 1672 } 1673 1674 /** 1675 * Return whether the user can respond to this {@code Call} via an SMS message. 1676 * 1677 * @return true if the "Respond via SMS" feature should be enabled 1678 * for this incoming call. 1679 * 1680 * The general rule is that we *do* allow "Respond via SMS" except for 1681 * the few (relatively rare) cases where we know for sure it won't 1682 * work, namely: 1683 * - a bogus or blank incoming number 1684 * - a call from a SIP address 1685 * - a "call presentation" that doesn't allow the number to be revealed 1686 * 1687 * In all other cases, we allow the user to respond via SMS. 1688 * 1689 * Note that this behavior isn't perfect; for example we have no way 1690 * to detect whether the incoming call is from a landline (with most 1691 * networks at least), so we still enable this feature even though 1692 * SMSes to that number will silently fail. 1693 */ 1694 boolean isRespondViaSmsCapable() { 1695 if (mState != CallState.RINGING) { 1696 return false; 1697 } 1698 1699 if (getHandle() == null) { 1700 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in 1701 // other words, the user should not be able to see the incoming phone number. 1702 return false; 1703 } 1704 1705 if (mPhoneNumberUtilsAdapter.isUriNumber(getHandle().toString())) { 1706 // The incoming number is actually a URI (i.e. a SIP address), 1707 // not a regular PSTN phone number, and we can't send SMSes to 1708 // SIP addresses. 1709 // (TODO: That might still be possible eventually, though. Is 1710 // there some SIP-specific equivalent to sending a text message?) 1711 return false; 1712 } 1713 1714 // Is there a valid SMS application on the phone? 1715 if (SmsApplication.getDefaultRespondViaMessageApplication(mContext, 1716 true /*updateIfNeeded*/) == null) { 1717 return false; 1718 } 1719 1720 // TODO: with some carriers (in certain countries) you *can* actually 1721 // tell whether a given number is a mobile phone or not. So in that 1722 // case we could potentially return false here if the incoming call is 1723 // from a land line. 1724 1725 // If none of the above special cases apply, it's OK to enable the 1726 // "Respond via SMS" feature. 1727 return true; 1728 } 1729 1730 List<String> getCannedSmsResponses() { 1731 return mCannedSmsResponses; 1732 } 1733 1734 /** 1735 * We need to make sure that before we move a call to the disconnected state, it no 1736 * longer has any parent/child relationships. We want to do this to ensure that the InCall 1737 * Service always has the right data in the right order. We also want to do it in telecom so 1738 * that the insurance policy lives in the framework side of things. 1739 */ 1740 private void fixParentAfterDisconnect() { 1741 setParentCall(null); 1742 } 1743 1744 /** 1745 * @return True if the call is ringing, else logs the action name. 1746 */ 1747 private boolean isRinging(String actionName) { 1748 if (mState == CallState.RINGING) { 1749 return true; 1750 } 1751 1752 Log.i(this, "Request to %s a non-ringing call %s", actionName, this); 1753 return false; 1754 } 1755 1756 @SuppressWarnings("rawtypes") 1757 private void decrementAssociatedCallCount(ServiceBinder binder) { 1758 if (binder != null) { 1759 binder.decrementAssociatedCallCount(); 1760 } 1761 } 1762 1763 /** 1764 * Looks up contact information based on the current handle. 1765 */ 1766 private void startCallerInfoLookup() { 1767 mCallerInfo = null; 1768 mCallsManager.getCallerInfoLookupHelper().startLookup(mHandle, mCallerInfoQueryListener); 1769 } 1770 1771 /** 1772 * Saves the specified caller info if the specified token matches that of the last query 1773 * that was made. 1774 * 1775 * @param callerInfo The new caller information to set. 1776 */ 1777 private void setCallerInfo(Uri handle, CallerInfo callerInfo) { 1778 Trace.beginSection("setCallerInfo"); 1779 Preconditions.checkNotNull(callerInfo); 1780 1781 if (!handle.equals(mHandle)) { 1782 Log.i(this, "setCallerInfo received stale caller info for an old handle. Ignoring."); 1783 return; 1784 } 1785 1786 mCallerInfo = callerInfo; 1787 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo); 1788 1789 if (mCallerInfo.contactDisplayPhotoUri == null || 1790 mCallerInfo.cachedPhotoIcon != null || mCallerInfo.cachedPhoto != null) { 1791 for (Listener l : mListeners) { 1792 l.onCallerInfoChanged(this); 1793 } 1794 } 1795 1796 Trace.endSection(); 1797 } 1798 1799 public CallerInfo getCallerInfo() { 1800 return mCallerInfo; 1801 } 1802 1803 private void maybeLoadCannedSmsResponses() { 1804 if (mCallDirection == CALL_DIRECTION_INCOMING 1805 && isRespondViaSmsCapable() 1806 && !mCannedSmsResponsesLoadingStarted) { 1807 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages"); 1808 mCannedSmsResponsesLoadingStarted = true; 1809 mCallsManager.getRespondViaSmsManager().loadCannedTextMessages( 1810 new Response<Void, List<String>>() { 1811 @Override 1812 public void onResult(Void request, List<String>... result) { 1813 if (result.length > 0) { 1814 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]); 1815 mCannedSmsResponses = result[0]; 1816 for (Listener l : mListeners) { 1817 l.onCannedSmsResponsesLoaded(Call.this); 1818 } 1819 } 1820 } 1821 1822 @Override 1823 public void onError(Void request, int code, String msg) { 1824 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code, 1825 msg); 1826 } 1827 }, 1828 mContext 1829 ); 1830 } else { 1831 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing"); 1832 } 1833 } 1834 1835 /** 1836 * Sets speakerphone option on when call begins. 1837 */ 1838 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) { 1839 mSpeakerphoneOn = startWithSpeakerphone; 1840 } 1841 1842 /** 1843 * Returns speakerphone option. 1844 * 1845 * @return Whether or not speakerphone should be set automatically when call begins. 1846 */ 1847 public boolean getStartWithSpeakerphoneOn() { 1848 return mSpeakerphoneOn; 1849 } 1850 1851 /** 1852 * Sets a video call provider for the call. 1853 */ 1854 public void setVideoProvider(IVideoProvider videoProvider) { 1855 Log.v(this, "setVideoProvider"); 1856 1857 if (videoProvider != null ) { 1858 try { 1859 mVideoProviderProxy = new VideoProviderProxy(mLock, videoProvider, this); 1860 } catch (RemoteException ignored) { 1861 // Ignore RemoteException. 1862 } 1863 } else { 1864 mVideoProviderProxy = null; 1865 } 1866 1867 mVideoProvider = videoProvider; 1868 1869 for (Listener l : mListeners) { 1870 l.onVideoCallProviderChanged(Call.this); 1871 } 1872 } 1873 1874 /** 1875 * @return The {@link Connection.VideoProvider} binder. 1876 */ 1877 public IVideoProvider getVideoProvider() { 1878 if (mVideoProviderProxy == null) { 1879 return null; 1880 } 1881 1882 return mVideoProviderProxy.getInterface(); 1883 } 1884 1885 /** 1886 * @return The {@link VideoProviderProxy} for this call. 1887 */ 1888 public VideoProviderProxy getVideoProviderProxy() { 1889 return mVideoProviderProxy; 1890 } 1891 1892 /** 1893 * The current video state for the call. 1894 * See {@link VideoProfile} for a list of valid video states. 1895 */ 1896 public int getVideoState() { 1897 return mVideoState; 1898 } 1899 1900 /** 1901 * Returns the video states which were applicable over the duration of a call. 1902 * See {@link VideoProfile} for a list of valid video states. 1903 * 1904 * @return The video states applicable over the duration of the call. 1905 */ 1906 public int getVideoStateHistory() { 1907 return mVideoStateHistory; 1908 } 1909 1910 /** 1911 * Determines the current video state for the call. 1912 * For an outgoing call determines the desired video state for the call. 1913 * Valid values: see {@link VideoProfile} 1914 * 1915 * @param videoState The video state for the call. 1916 */ 1917 public void setVideoState(int videoState) { 1918 // Track which video states were applicable over the duration of the call. 1919 // Only track the call state when the call is active or disconnected. This ensures we do 1920 // not include the video state when: 1921 // - Call is incoming (but not answered). 1922 // - Call it outgoing (but not answered). 1923 // We include the video state when disconnected to ensure that rejected calls reflect the 1924 // appropriate video state. 1925 if (isActive() || getState() == CallState.DISCONNECTED) { 1926 mVideoStateHistory = mVideoStateHistory | videoState; 1927 } 1928 1929 int previousVideoState = mVideoState; 1930 mVideoState = videoState; 1931 if (mVideoState != previousVideoState) { 1932 Log.event(this, Log.Events.VIDEO_STATE_CHANGED, 1933 VideoProfile.videoStateToString(videoState)); 1934 for (Listener l : mListeners) { 1935 l.onVideoStateChanged(this, previousVideoState, mVideoState); 1936 } 1937 } 1938 1939 if (VideoProfile.isVideo(videoState)) { 1940 mAnalytics.setCallIsVideo(true); 1941 } 1942 } 1943 1944 public boolean getIsVoipAudioMode() { 1945 return mIsVoipAudioMode; 1946 } 1947 1948 public void setIsVoipAudioMode(boolean audioModeIsVoip) { 1949 mIsVoipAudioMode = audioModeIsVoip; 1950 for (Listener l : mListeners) { 1951 l.onIsVoipAudioModeChanged(this); 1952 } 1953 } 1954 1955 public StatusHints getStatusHints() { 1956 return mStatusHints; 1957 } 1958 1959 public void setStatusHints(StatusHints statusHints) { 1960 mStatusHints = statusHints; 1961 for (Listener l : mListeners) { 1962 l.onStatusHintsChanged(this); 1963 } 1964 } 1965 1966 public boolean isUnknown() { 1967 return mCallDirection == CALL_DIRECTION_UNKNOWN; 1968 } 1969 1970 /** 1971 * Determines if this call is in a disconnecting state. 1972 * 1973 * @return {@code true} if this call is locally disconnecting. 1974 */ 1975 public boolean isLocallyDisconnecting() { 1976 return mIsLocallyDisconnecting; 1977 } 1978 1979 /** 1980 * Sets whether this call is in a disconnecting state. 1981 * 1982 * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting. 1983 */ 1984 private void setLocallyDisconnecting(boolean isLocallyDisconnecting) { 1985 mIsLocallyDisconnecting = isLocallyDisconnecting; 1986 } 1987 1988 /** 1989 * @return user handle of user initiating the outgoing call. 1990 */ 1991 public UserHandle getInitiatingUser() { 1992 return mInitiatingUser; 1993 } 1994 1995 /** 1996 * Set the user handle of user initiating the outgoing call. 1997 * @param initiatingUser 1998 */ 1999 public void setInitiatingUser(UserHandle initiatingUser) { 2000 Preconditions.checkNotNull(initiatingUser); 2001 mInitiatingUser = initiatingUser; 2002 } 2003 2004 static int getStateFromConnectionState(int state) { 2005 switch (state) { 2006 case Connection.STATE_INITIALIZING: 2007 return CallState.CONNECTING; 2008 case Connection.STATE_ACTIVE: 2009 return CallState.ACTIVE; 2010 case Connection.STATE_DIALING: 2011 return CallState.DIALING; 2012 case Connection.STATE_PULLING_CALL: 2013 return CallState.PULLING; 2014 case Connection.STATE_DISCONNECTED: 2015 return CallState.DISCONNECTED; 2016 case Connection.STATE_HOLDING: 2017 return CallState.ON_HOLD; 2018 case Connection.STATE_NEW: 2019 return CallState.NEW; 2020 case Connection.STATE_RINGING: 2021 return CallState.RINGING; 2022 } 2023 return CallState.DISCONNECTED; 2024 } 2025 2026 /** 2027 * Determines if this call is in disconnected state and waiting to be destroyed. 2028 * 2029 * @return {@code true} if this call is disconected. 2030 */ 2031 public boolean isDisconnected() { 2032 return (getState() == CallState.DISCONNECTED || getState() == CallState.ABORTED); 2033 } 2034 2035 /** 2036 * Determines if this call has just been created and has not been configured properly yet. 2037 * 2038 * @return {@code true} if this call is new. 2039 */ 2040 public boolean isNew() { 2041 return getState() == CallState.NEW; 2042 } 2043 2044 /** 2045 * Sets the call data usage for the call. 2046 * 2047 * @param callDataUsage The new call data usage (in bytes). 2048 */ 2049 public void setCallDataUsage(long callDataUsage) { 2050 mCallDataUsage = callDataUsage; 2051 } 2052 2053 /** 2054 * Returns the call data usage for the call. 2055 * 2056 * @return The call data usage (in bytes). 2057 */ 2058 public long getCallDataUsage() { 2059 return mCallDataUsage; 2060 } 2061 2062 /** 2063 * Returns true if the call is outgoing and the NEW_OUTGOING_CALL ordered broadcast intent 2064 * has come back to telecom and was processed. 2065 */ 2066 public boolean isNewOutgoingCallIntentBroadcastDone() { 2067 return mIsNewOutgoingCallIntentBroadcastDone; 2068 } 2069 2070 public void setNewOutgoingCallIntentBroadcastIsDone() { 2071 mIsNewOutgoingCallIntentBroadcastDone = true; 2072 } 2073 2074 /** 2075 * Determines if the call has been held by the remote party. 2076 * 2077 * @return {@code true} if the call is remotely held, {@code false} otherwise. 2078 */ 2079 public boolean isRemotelyHeld() { 2080 return mIsRemotelyHeld; 2081 } 2082 2083 /** 2084 * Handles Connection events received from a {@link ConnectionService}. 2085 * 2086 * @param event The event. 2087 * @param extras The extras. 2088 */ 2089 public void onConnectionEvent(String event, Bundle extras) { 2090 if (Connection.EVENT_ON_HOLD_TONE_START.equals(event)) { 2091 mIsRemotelyHeld = true; 2092 Log.event(this, Log.Events.REMOTELY_HELD); 2093 // Inform listeners of the fact that a call hold tone was received. This will trigger 2094 // the CallAudioManager to play a tone via the InCallTonePlayer. 2095 for (Listener l : mListeners) { 2096 l.onHoldToneRequested(this); 2097 } 2098 } else if (Connection.EVENT_ON_HOLD_TONE_END.equals(event)) { 2099 mIsRemotelyHeld = false; 2100 Log.event(this, Log.Events.REMOTELY_UNHELD); 2101 for (Listener l : mListeners) { 2102 l.onHoldToneRequested(this); 2103 } 2104 } else { 2105 for (Listener l : mListeners) { 2106 l.onConnectionEvent(this, event, extras); 2107 } 2108 } 2109 } 2110 2111 /** 2112 * Determines if a {@link Call}'s capabilities bitmask indicates that video is supported either 2113 * remotely or locally. 2114 * 2115 * @param capabilities The {@link Connection} capabilities for the call. 2116 * @return {@code true} if video is supported, {@code false} otherwise. 2117 */ 2118 private boolean doesCallSupportVideo(int capabilities) { 2119 return (capabilities & Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL) != 0 || 2120 (capabilities & Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL) != 0; 2121 } 2122 2123 /** 2124 * Remove any video capabilities set on a {@link Connection} capabilities bitmask. 2125 * 2126 * @param capabilities The capabilities. 2127 * @return The bitmask with video capabilities removed. 2128 */ 2129 private int removeVideoCapabilities(int capabilities) { 2130 return capabilities & ~(Connection.CAPABILITY_SUPPORTS_VT_LOCAL_BIDIRECTIONAL | 2131 Connection.CAPABILITY_SUPPORTS_VT_REMOTE_BIDIRECTIONAL); 2132 } 2133} 2134