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