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