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