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