Call.java revision 9644c42647f39d710d9d2900cdf6400e00e23ffe
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.provider.ContactsContract.Contacts; 26import android.telecom.CallState; 27import android.telecom.DisconnectCause; 28import android.telecom.Connection; 29import android.telecom.GatewayInfo; 30import android.telecom.ParcelableConnection; 31import android.telecom.PhoneAccount; 32import android.telecom.PhoneAccountHandle; 33import android.telecom.PhoneCapabilities; 34import android.telecom.Response; 35import android.telecom.StatusHints; 36import android.telecom.TelecomManager; 37import android.telecom.VideoProfile; 38import android.telephony.PhoneNumberUtils; 39import android.text.TextUtils; 40 41import com.android.internal.telecom.IVideoProvider; 42import com.android.internal.telephony.CallerInfo; 43import com.android.internal.telephony.CallerInfoAsyncQuery; 44import com.android.internal.telephony.CallerInfoAsyncQuery.OnQueryCompleteListener; 45import com.android.internal.telephony.SmsApplication; 46import com.android.server.telecom.ContactsAsyncHelper.OnImageLoadCompleteListener; 47 48import com.android.internal.util.Preconditions; 49 50import java.util.ArrayList; 51import java.util.Collections; 52import java.util.LinkedList; 53import java.util.List; 54import java.util.Locale; 55import java.util.Objects; 56import java.util.Set; 57import java.util.concurrent.ConcurrentHashMap; 58 59/** 60 * Encapsulates all aspects of a given phone call throughout its lifecycle, starting 61 * from the time the call intent was received by Telecom (vs. the time the call was 62 * connected etc). 63 */ 64final class Call implements CreateConnectionResponse { 65 /** 66 * Listener for events on the call. 67 */ 68 interface Listener { 69 void onSuccessfulOutgoingCall(Call call, int callState); 70 void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause); 71 void onSuccessfulIncomingCall(Call call); 72 void onFailedIncomingCall(Call call); 73 void onSuccessfulUnknownCall(Call call, int callState); 74 void onFailedUnknownCall(Call call); 75 void onRingbackRequested(Call call, boolean ringbackRequested); 76 void onPostDialWait(Call call, String remaining); 77 void onCallCapabilitiesChanged(Call call); 78 void onParentChanged(Call call); 79 void onChildrenChanged(Call call); 80 void onCannedSmsResponsesLoaded(Call call); 81 void onVideoCallProviderChanged(Call call); 82 void onCallerInfoChanged(Call call); 83 void onIsVoipAudioModeChanged(Call call); 84 void onStatusHintsChanged(Call call); 85 void onHandleChanged(Call call); 86 void onCallerDisplayNameChanged(Call call); 87 void onVideoStateChanged(Call call); 88 void onTargetPhoneAccountChanged(Call call); 89 void onConnectionManagerPhoneAccountChanged(Call call); 90 void onPhoneAccountChanged(Call call); 91 void onConferenceableCallsChanged(Call call); 92 boolean onCanceledViaNewOutgoingCallBroadcast(Call call); 93 } 94 95 abstract static class ListenerBase implements Listener { 96 @Override 97 public void onSuccessfulOutgoingCall(Call call, int callState) {} 98 @Override 99 public void onFailedOutgoingCall(Call call, DisconnectCause disconnectCause) {} 100 @Override 101 public void onSuccessfulIncomingCall(Call call) {} 102 @Override 103 public void onFailedIncomingCall(Call call) {} 104 @Override 105 public void onSuccessfulUnknownCall(Call call, int callState) {} 106 @Override 107 public void onFailedUnknownCall(Call call) {} 108 @Override 109 public void onRingbackRequested(Call call, boolean ringbackRequested) {} 110 @Override 111 public void onPostDialWait(Call call, String remaining) {} 112 @Override 113 public void onCallCapabilitiesChanged(Call call) {} 114 @Override 115 public void onParentChanged(Call call) {} 116 @Override 117 public void onChildrenChanged(Call call) {} 118 @Override 119 public void onCannedSmsResponsesLoaded(Call call) {} 120 @Override 121 public void onVideoCallProviderChanged(Call call) {} 122 @Override 123 public void onCallerInfoChanged(Call call) {} 124 @Override 125 public void onIsVoipAudioModeChanged(Call call) {} 126 @Override 127 public void onStatusHintsChanged(Call call) {} 128 @Override 129 public void onHandleChanged(Call call) {} 130 @Override 131 public void onCallerDisplayNameChanged(Call call) {} 132 @Override 133 public void onVideoStateChanged(Call call) {} 134 @Override 135 public void onTargetPhoneAccountChanged(Call call) {} 136 @Override 137 public void onConnectionManagerPhoneAccountChanged(Call call) {} 138 @Override 139 public void onPhoneAccountChanged(Call call) {} 140 @Override 141 public void onConferenceableCallsChanged(Call call) {} 142 @Override 143 public boolean onCanceledViaNewOutgoingCallBroadcast(Call call) { 144 return false; 145 } 146 } 147 148 private static final OnQueryCompleteListener sCallerInfoQueryListener = 149 new OnQueryCompleteListener() { 150 /** ${inheritDoc} */ 151 @Override 152 public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) { 153 if (cookie != null) { 154 ((Call) cookie).setCallerInfo(callerInfo, token); 155 } 156 } 157 }; 158 159 private static final OnImageLoadCompleteListener sPhotoLoadListener = 160 new OnImageLoadCompleteListener() { 161 /** ${inheritDoc} */ 162 @Override 163 public void onImageLoadComplete( 164 int token, Drawable photo, Bitmap photoIcon, Object cookie) { 165 if (cookie != null) { 166 ((Call) cookie).setPhoto(photo, photoIcon, token); 167 } 168 } 169 }; 170 171 private final Runnable mDirectToVoicemailRunnable = new Runnable() { 172 @Override 173 public void run() { 174 processDirectToVoicemail(); 175 } 176 }; 177 178 /** True if this is an incoming call. */ 179 private final boolean mIsIncoming; 180 181 /** True if this is a currently unknown call that was not previously tracked by CallsManager, 182 * and did not originate via the regular incoming/outgoing call code paths. 183 */ 184 private boolean mIsUnknown; 185 186 /** 187 * The time this call was created. Beyond logging and such, may also be used for bookkeeping 188 * and specifically for marking certain call attempts as failed attempts. 189 */ 190 private long mCreationTimeMillis = System.currentTimeMillis(); 191 192 /** The gateway information associated with this call. This stores the original call handle 193 * that the user is attempting to connect to via the gateway, the actual handle to dial in 194 * order to connect the call via the gateway, as well as the package name of the gateway 195 * service. */ 196 private GatewayInfo mGatewayInfo; 197 198 private PhoneAccountHandle mConnectionManagerPhoneAccountHandle; 199 200 private PhoneAccountHandle mTargetPhoneAccountHandle; 201 202 private final Handler mHandler = new Handler(); 203 204 private final List<Call> mConferenceableCalls = new ArrayList<>(); 205 206 private long mConnectTimeMillis = 0; 207 208 /** The state of the call. */ 209 private int mState; 210 211 /** The handle with which to establish this call. */ 212 private Uri mHandle; 213 214 /** 215 * The presentation requirements for the handle. See {@link TelecomManager} for valid values. 216 */ 217 private int mHandlePresentation; 218 219 /** The caller display name (CNAP) set by the connection service. */ 220 private String mCallerDisplayName; 221 222 /** 223 * The presentation requirements for the handle. See {@link TelecomManager} for valid values. 224 */ 225 private int mCallerDisplayNamePresentation; 226 227 /** 228 * The connection service which is attempted or already connecting this call. 229 */ 230 private ConnectionServiceWrapper mConnectionService; 231 232 private boolean mIsEmergencyCall; 233 234 private boolean mSpeakerphoneOn; 235 236 /** 237 * Tracks the video states which were applicable over the duration of a call. 238 * See {@link VideoProfile} for a list of valid video states. 239 */ 240 private int mVideoStateHistory; 241 242 private int mVideoState; 243 244 /** 245 * Disconnect cause for the call. Only valid if the state of the call is STATE_DISCONNECTED. 246 * See {@link android.telecom.DisconnectCause}. 247 */ 248 private DisconnectCause mDisconnectCause = new DisconnectCause(DisconnectCause.UNKNOWN); 249 250 /** Info used by the connection services. */ 251 private Bundle mExtras = Bundle.EMPTY; 252 253 /** Set of listeners on this call. 254 * 255 * ConcurrentHashMap constructor params: 8 is initial table size, 0.9f is 256 * load factor before resizing, 1 means we only expect a single thread to 257 * access the map so make only a single shard 258 */ 259 private final Set<Listener> mListeners = Collections.newSetFromMap( 260 new ConcurrentHashMap<Listener, Boolean>(8, 0.9f, 1)); 261 262 private CreateConnectionProcessor mCreateConnectionProcessor; 263 264 /** Caller information retrieved from the latest contact query. */ 265 private CallerInfo mCallerInfo; 266 267 /** The latest token used with a contact info query. */ 268 private int mQueryToken = 0; 269 270 /** Whether this call is requesting that Telecom play the ringback tone on its behalf. */ 271 private boolean mRingbackRequested = false; 272 273 /** Whether direct-to-voicemail query is pending. */ 274 private boolean mDirectToVoicemailQueryPending; 275 276 private int mCallCapabilities; 277 278 private boolean mIsConference = false; 279 280 private Call mParentCall = null; 281 282 private List<Call> mChildCalls = new LinkedList<>(); 283 284 /** Set of text message responses allowed for this call, if applicable. */ 285 private List<String> mCannedSmsResponses = Collections.EMPTY_LIST; 286 287 /** Whether an attempt has been made to load the text message responses. */ 288 private boolean mCannedSmsResponsesLoadingStarted = false; 289 290 private IVideoProvider mVideoProvider; 291 292 private boolean mIsVoipAudioMode; 293 private StatusHints mStatusHints; 294 private final ConnectionServiceRepository mRepository; 295 private final Context mContext; 296 297 private boolean mWasConferencePreviouslyMerged = false; 298 299 // For conferences which support merge/swap at their level, we retain a notion of an active call. 300 // This is used for BluetoothPhoneService. In order to support hold/merge, it must have the notion 301 // of the current "active" call within the conference call. This maintains the "active" call and 302 // switches every time the user hits "swap". 303 private Call mConferenceLevelActiveCall = null; 304 305 private boolean mIsLocallyDisconnecting = false; 306 307 /** 308 * Persists the specified parameters and initializes the new instance. 309 * 310 * @param context The context. 311 * @param repository The connection service repository. 312 * @param handle The handle to dial. 313 * @param gatewayInfo Gateway information to use for the call. 314 * @param connectionManagerPhoneAccountHandle Account to use for the service managing the call. 315 * This account must be one that was registered with the 316 * {@link PhoneAccount#CAPABILITY_CONNECTION_MANAGER} flag. 317 * @param targetPhoneAccountHandle Account information to use for the call. This account must be 318 * one that was registered with the {@link PhoneAccount#CAPABILITY_CALL_PROVIDER} flag. 319 * @param isIncoming True if this is an incoming call. 320 */ 321 Call( 322 Context context, 323 ConnectionServiceRepository repository, 324 Uri handle, 325 GatewayInfo gatewayInfo, 326 PhoneAccountHandle connectionManagerPhoneAccountHandle, 327 PhoneAccountHandle targetPhoneAccountHandle, 328 boolean isIncoming, 329 boolean isConference) { 330 mState = isConference ? CallState.ACTIVE : CallState.NEW; 331 mContext = context; 332 mRepository = repository; 333 setHandle(handle); 334 setHandle(handle, TelecomManager.PRESENTATION_ALLOWED); 335 mGatewayInfo = gatewayInfo; 336 setConnectionManagerPhoneAccount(connectionManagerPhoneAccountHandle); 337 setTargetPhoneAccount(targetPhoneAccountHandle); 338 mIsIncoming = isIncoming; 339 mIsConference = isConference; 340 maybeLoadCannedSmsResponses(); 341 } 342 343 void addListener(Listener listener) { 344 mListeners.add(listener); 345 } 346 347 void removeListener(Listener listener) { 348 if (listener != null) { 349 mListeners.remove(listener); 350 } 351 } 352 353 /** {@inheritDoc} */ 354 @Override 355 public String toString() { 356 String component = null; 357 if (mConnectionService != null && mConnectionService.getComponentName() != null) { 358 component = mConnectionService.getComponentName().flattenToShortString(); 359 } 360 361 return String.format(Locale.US, "[%s, %s, %s, %s, %d, childs(%d), has_parent(%b), [%s]", 362 System.identityHashCode(this), 363 CallState.toString(mState), 364 component, 365 Log.piiHandle(mHandle), 366 getVideoState(), 367 getChildCalls().size(), 368 getParentCall() != null, 369 PhoneCapabilities.toString(getCallCapabilities())); 370 } 371 372 int getState() { 373 return mState; 374 } 375 376 /** 377 * Sets the call state. Although there exists the notion of appropriate state transitions 378 * (see {@link CallState}), in practice those expectations break down when cellular systems 379 * misbehave and they do this very often. The result is that we do not enforce state transitions 380 * and instead keep the code resilient to unexpected state changes. 381 */ 382 void setState(int newState) { 383 if (mState != newState) { 384 Log.v(this, "setState %s -> %s", mState, newState); 385 mState = newState; 386 maybeLoadCannedSmsResponses(); 387 388 if (mState == CallState.DISCONNECTED) { 389 setLocallyDisconnecting(false); 390 fixParentAfterDisconnect(); 391 } 392 } 393 } 394 395 void setRingbackRequested(boolean ringbackRequested) { 396 mRingbackRequested = ringbackRequested; 397 for (Listener l : mListeners) { 398 l.onRingbackRequested(this, mRingbackRequested); 399 } 400 } 401 402 boolean isRingbackRequested() { 403 return mRingbackRequested; 404 } 405 406 boolean isConference() { 407 return mIsConference; 408 } 409 410 Uri getHandle() { 411 return mHandle; 412 } 413 414 int getHandlePresentation() { 415 return mHandlePresentation; 416 } 417 418 419 void setHandle(Uri handle) { 420 setHandle(handle, TelecomManager.PRESENTATION_ALLOWED); 421 } 422 423 void setHandle(Uri handle, int presentation) { 424 if (!Objects.equals(handle, mHandle) || presentation != mHandlePresentation) { 425 mHandlePresentation = presentation; 426 if (mHandlePresentation == TelecomManager.PRESENTATION_RESTRICTED || 427 mHandlePresentation == TelecomManager.PRESENTATION_UNKNOWN) { 428 mHandle = null; 429 } else { 430 mHandle = handle; 431 if (mHandle != null && !PhoneAccount.SCHEME_VOICEMAIL.equals(mHandle.getScheme()) 432 && TextUtils.isEmpty(mHandle.getSchemeSpecificPart())) { 433 // If the number is actually empty, set it to null, unless this is a 434 // SCHEME_VOICEMAIL uri which always has an empty number. 435 mHandle = null; 436 } 437 } 438 439 mIsEmergencyCall = mHandle != null && PhoneNumberUtils.isLocalEmergencyNumber(mContext, 440 mHandle.getSchemeSpecificPart()); 441 startCallerInfoLookup(); 442 for (Listener l : mListeners) { 443 l.onHandleChanged(this); 444 } 445 } 446 } 447 448 String getCallerDisplayName() { 449 return mCallerDisplayName; 450 } 451 452 int getCallerDisplayNamePresentation() { 453 return mCallerDisplayNamePresentation; 454 } 455 456 void setCallerDisplayName(String callerDisplayName, int presentation) { 457 if (!TextUtils.equals(callerDisplayName, mCallerDisplayName) || 458 presentation != mCallerDisplayNamePresentation) { 459 mCallerDisplayName = callerDisplayName; 460 mCallerDisplayNamePresentation = presentation; 461 for (Listener l : mListeners) { 462 l.onCallerDisplayNameChanged(this); 463 } 464 } 465 } 466 467 String getName() { 468 return mCallerInfo == null ? null : mCallerInfo.name; 469 } 470 471 Bitmap getPhotoIcon() { 472 return mCallerInfo == null ? null : mCallerInfo.cachedPhotoIcon; 473 } 474 475 Drawable getPhoto() { 476 return mCallerInfo == null ? null : mCallerInfo.cachedPhoto; 477 } 478 479 /** 480 * @param disconnectCause The reason for the disconnection, represented by 481 * {@link android.telecom.DisconnectCause}. 482 */ 483 void setDisconnectCause(DisconnectCause disconnectCause) { 484 // TODO: Consider combining this method with a setDisconnected() method that is totally 485 // separate from setState. 486 mDisconnectCause = disconnectCause; 487 } 488 489 DisconnectCause getDisconnectCause() { 490 return mDisconnectCause; 491 } 492 493 boolean isEmergencyCall() { 494 return mIsEmergencyCall; 495 } 496 497 /** 498 * @return The original handle this call is associated with. In-call services should use this 499 * handle when indicating in their UI the handle that is being called. 500 */ 501 public Uri getOriginalHandle() { 502 if (mGatewayInfo != null && !mGatewayInfo.isEmpty()) { 503 return mGatewayInfo.getOriginalAddress(); 504 } 505 return getHandle(); 506 } 507 508 GatewayInfo getGatewayInfo() { 509 return mGatewayInfo; 510 } 511 512 void setGatewayInfo(GatewayInfo gatewayInfo) { 513 mGatewayInfo = gatewayInfo; 514 } 515 516 PhoneAccountHandle getConnectionManagerPhoneAccount() { 517 return mConnectionManagerPhoneAccountHandle; 518 } 519 520 void setConnectionManagerPhoneAccount(PhoneAccountHandle accountHandle) { 521 if (!Objects.equals(mConnectionManagerPhoneAccountHandle, accountHandle)) { 522 mConnectionManagerPhoneAccountHandle = accountHandle; 523 for (Listener l : mListeners) { 524 l.onConnectionManagerPhoneAccountChanged(this); 525 } 526 } 527 528 } 529 530 PhoneAccountHandle getTargetPhoneAccount() { 531 return mTargetPhoneAccountHandle; 532 } 533 534 void setTargetPhoneAccount(PhoneAccountHandle accountHandle) { 535 if (!Objects.equals(mTargetPhoneAccountHandle, accountHandle)) { 536 mTargetPhoneAccountHandle = accountHandle; 537 for (Listener l : mListeners) { 538 l.onTargetPhoneAccountChanged(this); 539 } 540 } 541 } 542 543 boolean isIncoming() { 544 return mIsIncoming; 545 } 546 547 /** 548 * @return The "age" of this call object in milliseconds, which typically also represents the 549 * period since this call was added to the set pending outgoing calls, see 550 * mCreationTimeMillis. 551 */ 552 long getAgeMillis() { 553 return System.currentTimeMillis() - mCreationTimeMillis; 554 } 555 556 /** 557 * @return The time when this call object was created and added to the set of pending outgoing 558 * calls. 559 */ 560 long getCreationTimeMillis() { 561 return mCreationTimeMillis; 562 } 563 564 void setCreationTimeMillis(long time) { 565 mCreationTimeMillis = time; 566 } 567 568 long getConnectTimeMillis() { 569 return mConnectTimeMillis; 570 } 571 572 void setConnectTimeMillis(long connectTimeMillis) { 573 mConnectTimeMillis = connectTimeMillis; 574 } 575 576 int getCallCapabilities() { 577 return mCallCapabilities; 578 } 579 580 void setCallCapabilities(int callCapabilities) { 581 setCallCapabilities(callCapabilities, false /* forceUpdate */); 582 } 583 584 void setCallCapabilities(int callCapabilities, boolean forceUpdate) { 585 Log.v(this, "setCallCapabilities: %s", PhoneCapabilities.toString(callCapabilities)); 586 if (forceUpdate || mCallCapabilities != callCapabilities) { 587 mCallCapabilities = callCapabilities; 588 for (Listener l : mListeners) { 589 l.onCallCapabilitiesChanged(this); 590 } 591 } 592 } 593 594 Call getParentCall() { 595 return mParentCall; 596 } 597 598 List<Call> getChildCalls() { 599 return mChildCalls; 600 } 601 602 boolean wasConferencePreviouslyMerged() { 603 return mWasConferencePreviouslyMerged; 604 } 605 606 Call getConferenceLevelActiveCall() { 607 return mConferenceLevelActiveCall; 608 } 609 610 ConnectionServiceWrapper getConnectionService() { 611 return mConnectionService; 612 } 613 614 /** 615 * Retrieves the {@link Context} for the call. 616 * 617 * @return The {@link Context}. 618 */ 619 Context getContext() { 620 return mContext; 621 } 622 623 void setConnectionService(ConnectionServiceWrapper service) { 624 Preconditions.checkNotNull(service); 625 626 clearConnectionService(); 627 628 service.incrementAssociatedCallCount(); 629 mConnectionService = service; 630 mConnectionService.addCall(this); 631 } 632 633 /** 634 * Clears the associated connection service. 635 */ 636 void clearConnectionService() { 637 if (mConnectionService != null) { 638 ConnectionServiceWrapper serviceTemp = mConnectionService; 639 mConnectionService = null; 640 serviceTemp.removeCall(this); 641 642 // Decrementing the count can cause the service to unbind, which itself can trigger the 643 // service-death code. Since the service death code tries to clean up any associated 644 // calls, we need to make sure to remove that information (e.g., removeCall()) before 645 // we decrement. Technically, invoking removeCall() prior to decrementing is all that is 646 // necessary, but cleaning up mConnectionService prior to triggering an unbind is good 647 // to do. 648 decrementAssociatedCallCount(serviceTemp); 649 } 650 } 651 652 private void processDirectToVoicemail() { 653 if (mDirectToVoicemailQueryPending) { 654 if (mCallerInfo != null && mCallerInfo.shouldSendToVoicemail) { 655 Log.i(this, "Directing call to voicemail: %s.", this); 656 // TODO: Once we move State handling from CallsManager to Call, we 657 // will not need to set STATE_RINGING state prior to calling reject. 658 setState(CallState.RINGING); 659 reject(false, null); 660 } else { 661 // TODO: Make this class (not CallsManager) responsible for changing 662 // the call state to STATE_RINGING. 663 664 // TODO: Replace this with state transition to STATE_RINGING. 665 for (Listener l : mListeners) { 666 l.onSuccessfulIncomingCall(this); 667 } 668 } 669 670 mDirectToVoicemailQueryPending = false; 671 } 672 } 673 674 /** 675 * Starts the create connection sequence. Upon completion, there should exist an active 676 * connection through a connection service (or the call will have failed). 677 * 678 * @param phoneAccountRegistrar The phone account registrar. 679 */ 680 void startCreateConnection(PhoneAccountRegistrar phoneAccountRegistrar) { 681 Preconditions.checkState(mCreateConnectionProcessor == null); 682 mCreateConnectionProcessor = new CreateConnectionProcessor(this, mRepository, this, 683 phoneAccountRegistrar, mContext); 684 mCreateConnectionProcessor.process(); 685 } 686 687 @Override 688 public void handleCreateConnectionSuccess( 689 CallIdMapper idMapper, 690 ParcelableConnection connection) { 691 Log.v(this, "handleCreateConnectionSuccessful %s", connection); 692 mCreateConnectionProcessor = null; 693 setTargetPhoneAccount(connection.getPhoneAccount()); 694 setHandle(connection.getHandle(), connection.getHandlePresentation()); 695 setCallerDisplayName( 696 connection.getCallerDisplayName(), connection.getCallerDisplayNamePresentation()); 697 setCallCapabilities(connection.getCapabilities()); 698 setVideoProvider(connection.getVideoProvider()); 699 setVideoState(connection.getVideoState()); 700 setRingbackRequested(connection.isRingbackRequested()); 701 setIsVoipAudioMode(connection.getIsVoipAudioMode()); 702 setStatusHints(connection.getStatusHints()); 703 704 mConferenceableCalls.clear(); 705 for (String id : connection.getConferenceableConnectionIds()) { 706 mConferenceableCalls.add(idMapper.getCall(id)); 707 } 708 709 if (mIsUnknown) { 710 for (Listener l : mListeners) { 711 l.onSuccessfulUnknownCall(this, getStateFromConnectionState(connection.getState())); 712 } 713 } else if (mIsIncoming) { 714 // We do not handle incoming calls immediately when they are verified by the connection 715 // service. We allow the caller-info-query code to execute first so that we can read the 716 // direct-to-voicemail property before deciding if we want to show the incoming call to 717 // the user or if we want to reject the call. 718 mDirectToVoicemailQueryPending = true; 719 720 // Timeout the direct-to-voicemail lookup execution so that we dont wait too long before 721 // showing the user the incoming call screen. 722 mHandler.postDelayed(mDirectToVoicemailRunnable, Timeouts.getDirectToVoicemailMillis( 723 mContext.getContentResolver())); 724 } else { 725 for (Listener l : mListeners) { 726 l.onSuccessfulOutgoingCall(this, 727 getStateFromConnectionState(connection.getState())); 728 } 729 } 730 } 731 732 @Override 733 public void handleCreateConnectionFailure(DisconnectCause disconnectCause) { 734 mCreateConnectionProcessor = null; 735 clearConnectionService(); 736 setDisconnectCause(disconnectCause); 737 CallsManager.getInstance().markCallAsDisconnected(this, disconnectCause); 738 739 if (mIsUnknown) { 740 for (Listener listener : mListeners) { 741 listener.onFailedUnknownCall(this); 742 } 743 } else if (mIsIncoming) { 744 for (Listener listener : mListeners) { 745 listener.onFailedIncomingCall(this); 746 } 747 } else { 748 for (Listener listener : mListeners) { 749 listener.onFailedOutgoingCall(this, disconnectCause); 750 } 751 } 752 } 753 754 /** 755 * Plays the specified DTMF tone. 756 */ 757 void playDtmfTone(char digit) { 758 if (mConnectionService == null) { 759 Log.w(this, "playDtmfTone() request on a call without a connection service."); 760 } else { 761 Log.i(this, "Send playDtmfTone to connection service for call %s", this); 762 mConnectionService.playDtmfTone(this, digit); 763 } 764 } 765 766 /** 767 * Stops playing any currently playing DTMF tone. 768 */ 769 void stopDtmfTone() { 770 if (mConnectionService == null) { 771 Log.w(this, "stopDtmfTone() request on a call without a connection service."); 772 } else { 773 Log.i(this, "Send stopDtmfTone to connection service for call %s", this); 774 mConnectionService.stopDtmfTone(this); 775 } 776 } 777 778 void disconnect() { 779 disconnect(false); 780 } 781 782 /** 783 * Attempts to disconnect the call through the connection service. 784 */ 785 void disconnect(boolean wasViaNewOutgoingCallBroadcaster) { 786 // Track that the call is now locally disconnecting. 787 setLocallyDisconnecting(true); 788 789 if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT || 790 mState == CallState.CONNECTING) { 791 Log.v(this, "Aborting call %s", this); 792 abort(wasViaNewOutgoingCallBroadcaster); 793 } else if (mState != CallState.ABORTED && mState != CallState.DISCONNECTED) { 794 if (mConnectionService == null) { 795 Log.e(this, new Exception(), "disconnect() request on a call without a" 796 + " connection service."); 797 } else { 798 Log.i(this, "Send disconnect to connection service for call: %s", this); 799 // The call isn't officially disconnected until the connection service 800 // confirms that the call was actually disconnected. Only then is the 801 // association between call and connection service severed, see 802 // {@link CallsManager#markCallAsDisconnected}. 803 mConnectionService.disconnect(this); 804 } 805 } 806 } 807 808 void abort(boolean wasViaNewOutgoingCallBroadcaster) { 809 if (mCreateConnectionProcessor != null) { 810 mCreateConnectionProcessor.abort(); 811 } else if (mState == CallState.NEW || mState == CallState.PRE_DIAL_WAIT 812 || mState == CallState.CONNECTING) { 813 if (wasViaNewOutgoingCallBroadcaster) { 814 // If the cancelation was from NEW_OUTGOING_CALL, then we do not automatically 815 // destroy the call. Instead, we announce the cancelation and CallsManager handles 816 // it through a timer. Since apps often cancel calls through NEW_OUTGOING_CALL and 817 // then re-dial them quickly using a gateway, allowing the first call to end 818 // causes jank. This timeout allows CallsManager to transition the first call into 819 // the second call so that in-call only ever sees a single call...eliminating the 820 // jank altogether. 821 for (Listener listener : mListeners) { 822 if (listener.onCanceledViaNewOutgoingCallBroadcast(this)) { 823 // The first listener to handle this wins. A return value of true means that 824 // the listener will handle the disconnection process later and so we 825 // should not continue it here. 826 setLocallyDisconnecting(false); 827 return; 828 } 829 } 830 } 831 832 handleCreateConnectionFailure(new DisconnectCause(DisconnectCause.CANCELED)); 833 } else { 834 Log.v(this, "Cannot abort a call which isn't either PRE_DIAL_WAIT or CONNECTING"); 835 } 836 } 837 838 /** 839 * Answers the call if it is ringing. 840 * 841 * @param videoState The video state in which to answer the call. 842 */ 843 void answer(int videoState) { 844 Preconditions.checkNotNull(mConnectionService); 845 846 // Check to verify that the call is still in the ringing state. A call can change states 847 // between the time the user hits 'answer' and Telecom receives the command. 848 if (isRinging("answer")) { 849 // At this point, we are asking the connection service to answer but we don't assume 850 // that it will work. Instead, we wait until confirmation from the connectino service 851 // that the call is in a non-STATE_RINGING state before changing the UI. See 852 // {@link ConnectionServiceAdapter#setActive} and other set* methods. 853 mConnectionService.answer(this, videoState); 854 } 855 } 856 857 /** 858 * Rejects the call if it is ringing. 859 * 860 * @param rejectWithMessage Whether to send a text message as part of the call rejection. 861 * @param textMessage An optional text message to send as part of the rejection. 862 */ 863 void reject(boolean rejectWithMessage, String textMessage) { 864 Preconditions.checkNotNull(mConnectionService); 865 866 // Check to verify that the call is still in the ringing state. A call can change states 867 // between the time the user hits 'reject' and Telecomm receives the command. 868 if (isRinging("reject")) { 869 mConnectionService.reject(this); 870 } 871 } 872 873 /** 874 * Puts the call on hold if it is currently active. 875 */ 876 void hold() { 877 Preconditions.checkNotNull(mConnectionService); 878 879 if (mState == CallState.ACTIVE) { 880 mConnectionService.hold(this); 881 } 882 } 883 884 /** 885 * Releases the call from hold if it is currently active. 886 */ 887 void unhold() { 888 Preconditions.checkNotNull(mConnectionService); 889 890 if (mState == CallState.ON_HOLD) { 891 mConnectionService.unhold(this); 892 } 893 } 894 895 /** Checks if this is a live call or not. */ 896 boolean isAlive() { 897 switch (mState) { 898 case CallState.NEW: 899 case CallState.RINGING: 900 case CallState.DISCONNECTED: 901 case CallState.ABORTED: 902 return false; 903 default: 904 return true; 905 } 906 } 907 908 boolean isActive() { 909 return mState == CallState.ACTIVE; 910 } 911 912 Bundle getExtras() { 913 return mExtras; 914 } 915 916 void setExtras(Bundle extras) { 917 mExtras = extras; 918 } 919 920 /** 921 * @return the uri of the contact associated with this call. 922 */ 923 Uri getContactUri() { 924 if (mCallerInfo == null || !mCallerInfo.contactExists) { 925 return getHandle(); 926 } 927 return Contacts.getLookupUri(mCallerInfo.contactIdOrZero, mCallerInfo.lookupKey); 928 } 929 930 Uri getRingtone() { 931 return mCallerInfo == null ? null : mCallerInfo.contactRingtoneUri; 932 } 933 934 void onPostDialWait(String remaining) { 935 for (Listener l : mListeners) { 936 l.onPostDialWait(this, remaining); 937 } 938 } 939 940 void postDialContinue(boolean proceed) { 941 mConnectionService.onPostDialContinue(this, proceed); 942 } 943 944 void conferenceWith(Call otherCall) { 945 if (mConnectionService == null) { 946 Log.w(this, "conference requested on a call without a connection service."); 947 } else { 948 mConnectionService.conference(this, otherCall); 949 } 950 } 951 952 void splitFromConference() { 953 if (mConnectionService == null) { 954 Log.w(this, "splitting from conference call without a connection service"); 955 } else { 956 mConnectionService.splitFromConference(this); 957 } 958 } 959 960 void mergeConference() { 961 if (mConnectionService == null) { 962 Log.w(this, "merging conference calls without a connection service."); 963 } else if (can(PhoneCapabilities.MERGE_CONFERENCE)) { 964 mConnectionService.mergeConference(this); 965 mWasConferencePreviouslyMerged = true; 966 } 967 } 968 969 void swapConference() { 970 if (mConnectionService == null) { 971 Log.w(this, "swapping conference calls without a connection service."); 972 } else if (can(PhoneCapabilities.SWAP_CONFERENCE)) { 973 mConnectionService.swapConference(this); 974 switch (mChildCalls.size()) { 975 case 1: 976 mConferenceLevelActiveCall = mChildCalls.get(0); 977 break; 978 case 2: 979 // swap 980 mConferenceLevelActiveCall = mChildCalls.get(0) == mConferenceLevelActiveCall ? 981 mChildCalls.get(1) : mChildCalls.get(0); 982 break; 983 default: 984 // For anything else 0, or 3+, set it to null since it is impossible to tell. 985 mConferenceLevelActiveCall = null; 986 break; 987 } 988 } 989 } 990 991 void setParentCall(Call parentCall) { 992 if (parentCall == this) { 993 Log.e(this, new Exception(), "setting the parent to self"); 994 return; 995 } 996 if (parentCall == mParentCall) { 997 // nothing to do 998 return; 999 } 1000 Preconditions.checkState(parentCall == null || mParentCall == null); 1001 1002 Call oldParent = mParentCall; 1003 if (mParentCall != null) { 1004 mParentCall.removeChildCall(this); 1005 } 1006 mParentCall = parentCall; 1007 if (mParentCall != null) { 1008 mParentCall.addChildCall(this); 1009 } 1010 1011 for (Listener l : mListeners) { 1012 l.onParentChanged(this); 1013 } 1014 } 1015 1016 void setConferenceableCalls(List<Call> conferenceableCalls) { 1017 mConferenceableCalls.clear(); 1018 mConferenceableCalls.addAll(conferenceableCalls); 1019 1020 for (Listener l : mListeners) { 1021 l.onConferenceableCallsChanged(this); 1022 } 1023 } 1024 1025 List<Call> getConferenceableCalls() { 1026 return mConferenceableCalls; 1027 } 1028 1029 boolean can(int capability) { 1030 return (mCallCapabilities & capability) == capability; 1031 } 1032 1033 private void addChildCall(Call call) { 1034 if (!mChildCalls.contains(call)) { 1035 // Set the pseudo-active call to the latest child added to the conference. 1036 // See definition of mConferenceLevelActiveCall for more detail. 1037 mConferenceLevelActiveCall = call; 1038 mChildCalls.add(call); 1039 1040 for (Listener l : mListeners) { 1041 l.onChildrenChanged(this); 1042 } 1043 } 1044 } 1045 1046 private void removeChildCall(Call call) { 1047 if (mChildCalls.remove(call)) { 1048 for (Listener l : mListeners) { 1049 l.onChildrenChanged(this); 1050 } 1051 } 1052 } 1053 1054 /** 1055 * Return whether the user can respond to this {@code Call} via an SMS message. 1056 * 1057 * @return true if the "Respond via SMS" feature should be enabled 1058 * for this incoming call. 1059 * 1060 * The general rule is that we *do* allow "Respond via SMS" except for 1061 * the few (relatively rare) cases where we know for sure it won't 1062 * work, namely: 1063 * - a bogus or blank incoming number 1064 * - a call from a SIP address 1065 * - a "call presentation" that doesn't allow the number to be revealed 1066 * 1067 * In all other cases, we allow the user to respond via SMS. 1068 * 1069 * Note that this behavior isn't perfect; for example we have no way 1070 * to detect whether the incoming call is from a landline (with most 1071 * networks at least), so we still enable this feature even though 1072 * SMSes to that number will silently fail. 1073 */ 1074 boolean isRespondViaSmsCapable() { 1075 if (mState != CallState.RINGING) { 1076 return false; 1077 } 1078 1079 if (getHandle() == null) { 1080 // No incoming number known or call presentation is "PRESENTATION_RESTRICTED", in 1081 // other words, the user should not be able to see the incoming phone number. 1082 return false; 1083 } 1084 1085 if (PhoneNumberUtils.isUriNumber(getHandle().toString())) { 1086 // The incoming number is actually a URI (i.e. a SIP address), 1087 // not a regular PSTN phone number, and we can't send SMSes to 1088 // SIP addresses. 1089 // (TODO: That might still be possible eventually, though. Is 1090 // there some SIP-specific equivalent to sending a text message?) 1091 return false; 1092 } 1093 1094 // Is there a valid SMS application on the phone? 1095 if (SmsApplication.getDefaultRespondViaMessageApplication(mContext, 1096 true /*updateIfNeeded*/) == null) { 1097 return false; 1098 } 1099 1100 // TODO: with some carriers (in certain countries) you *can* actually 1101 // tell whether a given number is a mobile phone or not. So in that 1102 // case we could potentially return false here if the incoming call is 1103 // from a land line. 1104 1105 // If none of the above special cases apply, it's OK to enable the 1106 // "Respond via SMS" feature. 1107 return true; 1108 } 1109 1110 List<String> getCannedSmsResponses() { 1111 return mCannedSmsResponses; 1112 } 1113 1114 /** 1115 * We need to make sure that before we move a call to the disconnected state, it no 1116 * longer has any parent/child relationships. We want to do this to ensure that the InCall 1117 * Service always has the right data in the right order. We also want to do it in telecom so 1118 * that the insurance policy lives in the framework side of things. 1119 */ 1120 private void fixParentAfterDisconnect() { 1121 setParentCall(null); 1122 } 1123 1124 /** 1125 * @return True if the call is ringing, else logs the action name. 1126 */ 1127 private boolean isRinging(String actionName) { 1128 if (mState == CallState.RINGING) { 1129 return true; 1130 } 1131 1132 Log.i(this, "Request to %s a non-ringing call %s", actionName, this); 1133 return false; 1134 } 1135 1136 @SuppressWarnings("rawtypes") 1137 private void decrementAssociatedCallCount(ServiceBinder binder) { 1138 if (binder != null) { 1139 binder.decrementAssociatedCallCount(); 1140 } 1141 } 1142 1143 /** 1144 * Looks up contact information based on the current handle. 1145 */ 1146 private void startCallerInfoLookup() { 1147 String number = mHandle == null ? null : mHandle.getSchemeSpecificPart(); 1148 1149 mQueryToken++; // Updated so that previous queries can no longer set the information. 1150 mCallerInfo = null; 1151 if (!TextUtils.isEmpty(number)) { 1152 Log.v(this, "Looking up information for: %s.", Log.piiHandle(number)); 1153 CallerInfoAsyncQuery.startQuery( 1154 mQueryToken, 1155 mContext, 1156 number, 1157 sCallerInfoQueryListener, 1158 this); 1159 } 1160 } 1161 1162 /** 1163 * Saves the specified caller info if the specified token matches that of the last query 1164 * that was made. 1165 * 1166 * @param callerInfo The new caller information to set. 1167 * @param token The token used with this query. 1168 */ 1169 private void setCallerInfo(CallerInfo callerInfo, int token) { 1170 Preconditions.checkNotNull(callerInfo); 1171 1172 if (mQueryToken == token) { 1173 mCallerInfo = callerInfo; 1174 Log.i(this, "CallerInfo received for %s: %s", Log.piiHandle(mHandle), callerInfo); 1175 1176 if (mCallerInfo.contactDisplayPhotoUri != null) { 1177 Log.d(this, "Searching person uri %s for call %s", 1178 mCallerInfo.contactDisplayPhotoUri, this); 1179 ContactsAsyncHelper.startObtainPhotoAsync( 1180 token, 1181 mContext, 1182 mCallerInfo.contactDisplayPhotoUri, 1183 sPhotoLoadListener, 1184 this); 1185 // Do not call onCallerInfoChanged yet in this case. We call it in setPhoto(). 1186 } else { 1187 for (Listener l : mListeners) { 1188 l.onCallerInfoChanged(this); 1189 } 1190 } 1191 1192 processDirectToVoicemail(); 1193 } 1194 } 1195 1196 CallerInfo getCallerInfo() { 1197 return mCallerInfo; 1198 } 1199 1200 /** 1201 * Saves the specified photo information if the specified token matches that of the last query. 1202 * 1203 * @param photo The photo as a drawable. 1204 * @param photoIcon The photo as a small icon. 1205 * @param token The token used with this query. 1206 */ 1207 private void setPhoto(Drawable photo, Bitmap photoIcon, int token) { 1208 if (mQueryToken == token) { 1209 mCallerInfo.cachedPhoto = photo; 1210 mCallerInfo.cachedPhotoIcon = photoIcon; 1211 1212 for (Listener l : mListeners) { 1213 l.onCallerInfoChanged(this); 1214 } 1215 } 1216 } 1217 1218 private void maybeLoadCannedSmsResponses() { 1219 if (mIsIncoming && isRespondViaSmsCapable() && !mCannedSmsResponsesLoadingStarted) { 1220 Log.d(this, "maybeLoadCannedSmsResponses: starting task to load messages"); 1221 mCannedSmsResponsesLoadingStarted = true; 1222 RespondViaSmsManager.getInstance().loadCannedTextMessages( 1223 new Response<Void, List<String>>() { 1224 @Override 1225 public void onResult(Void request, List<String>... result) { 1226 if (result.length > 0) { 1227 Log.d(this, "maybeLoadCannedSmsResponses: got %s", result[0]); 1228 mCannedSmsResponses = result[0]; 1229 for (Listener l : mListeners) { 1230 l.onCannedSmsResponsesLoaded(Call.this); 1231 } 1232 } 1233 } 1234 1235 @Override 1236 public void onError(Void request, int code, String msg) { 1237 Log.w(Call.this, "Error obtaining canned SMS responses: %d %s", code, 1238 msg); 1239 } 1240 }, 1241 mContext 1242 ); 1243 } else { 1244 Log.d(this, "maybeLoadCannedSmsResponses: doing nothing"); 1245 } 1246 } 1247 1248 /** 1249 * Sets speakerphone option on when call begins. 1250 */ 1251 public void setStartWithSpeakerphoneOn(boolean startWithSpeakerphone) { 1252 mSpeakerphoneOn = startWithSpeakerphone; 1253 } 1254 1255 /** 1256 * Returns speakerphone option. 1257 * 1258 * @return Whether or not speakerphone should be set automatically when call begins. 1259 */ 1260 public boolean getStartWithSpeakerphoneOn() { 1261 return mSpeakerphoneOn; 1262 } 1263 1264 /** 1265 * Sets a video call provider for the call. 1266 */ 1267 public void setVideoProvider(IVideoProvider videoProvider) { 1268 mVideoProvider = videoProvider; 1269 for (Listener l : mListeners) { 1270 l.onVideoCallProviderChanged(Call.this); 1271 } 1272 } 1273 1274 /** 1275 * @return Return the {@link Connection.VideoProvider} binder. 1276 */ 1277 public IVideoProvider getVideoProvider() { 1278 return mVideoProvider; 1279 } 1280 1281 /** 1282 * The current video state for the call. 1283 * Valid values: see {@link VideoProfile.VideoState}. 1284 */ 1285 public int getVideoState() { 1286 return mVideoState; 1287 } 1288 1289 /** 1290 * Returns the video states which were applicable over the duration of a call. 1291 * See {@link VideoProfile} for a list of valid video states. 1292 * 1293 * @return The video states applicable over the duration of the call. 1294 */ 1295 public int getVideoStateHistory() { 1296 return mVideoStateHistory; 1297 } 1298 1299 /** 1300 * Determines the current video state for the call. 1301 * For an outgoing call determines the desired video state for the call. 1302 * Valid values: see {@link VideoProfile.VideoState} 1303 * 1304 * @param videoState The video state for the call. 1305 */ 1306 public void setVideoState(int videoState) { 1307 // Track which video states were applicable over the duration of the call. 1308 mVideoStateHistory = mVideoStateHistory | videoState; 1309 1310 mVideoState = videoState; 1311 for (Listener l : mListeners) { 1312 l.onVideoStateChanged(this); 1313 } 1314 } 1315 1316 public boolean getIsVoipAudioMode() { 1317 return mIsVoipAudioMode; 1318 } 1319 1320 public void setIsVoipAudioMode(boolean audioModeIsVoip) { 1321 mIsVoipAudioMode = audioModeIsVoip; 1322 for (Listener l : mListeners) { 1323 l.onIsVoipAudioModeChanged(this); 1324 } 1325 } 1326 1327 public StatusHints getStatusHints() { 1328 return mStatusHints; 1329 } 1330 1331 public void setStatusHints(StatusHints statusHints) { 1332 mStatusHints = statusHints; 1333 for (Listener l : mListeners) { 1334 l.onStatusHintsChanged(this); 1335 } 1336 } 1337 1338 public boolean isUnknown() { 1339 return mIsUnknown; 1340 } 1341 1342 public void setIsUnknown(boolean isUnknown) { 1343 mIsUnknown = isUnknown; 1344 } 1345 1346 /** 1347 * Determines if this call is in a disconnecting state. 1348 * 1349 * @return {@code true} if this call is locally disconnecting. 1350 */ 1351 public boolean isLocallyDisconnecting() { 1352 return mIsLocallyDisconnecting; 1353 } 1354 1355 /** 1356 * Sets whether this call is in a disconnecting state. 1357 * 1358 * @param isLocallyDisconnecting {@code true} if this call is locally disconnecting. 1359 */ 1360 private void setLocallyDisconnecting(boolean isLocallyDisconnecting) { 1361 mIsLocallyDisconnecting = isLocallyDisconnecting; 1362 } 1363 1364 static int getStateFromConnectionState(int state) { 1365 switch (state) { 1366 case Connection.STATE_INITIALIZING: 1367 return CallState.CONNECTING; 1368 case Connection.STATE_ACTIVE: 1369 return CallState.ACTIVE; 1370 case Connection.STATE_DIALING: 1371 return CallState.DIALING; 1372 case Connection.STATE_DISCONNECTED: 1373 return CallState.DISCONNECTED; 1374 case Connection.STATE_HOLDING: 1375 return CallState.ON_HOLD; 1376 case Connection.STATE_NEW: 1377 return CallState.NEW; 1378 case Connection.STATE_RINGING: 1379 return CallState.RINGING; 1380 } 1381 return CallState.DISCONNECTED; 1382 } 1383} 1384