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