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