1 /* 2 * Copyright (C) 2013 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.incallui.call; 18 19 import android.content.Context; 20 import android.hardware.camera2.CameraCharacteristics; 21 import android.net.Uri; 22 import android.os.Build.VERSION; 23 import android.os.Build.VERSION_CODES; 24 import android.os.Bundle; 25 import android.os.Trace; 26 import android.support.annotation.IntDef; 27 import android.support.annotation.NonNull; 28 import android.support.annotation.Nullable; 29 import android.telecom.Call; 30 import android.telecom.Call.Details; 31 import android.telecom.CallAudioState; 32 import android.telecom.Connection; 33 import android.telecom.DisconnectCause; 34 import android.telecom.GatewayInfo; 35 import android.telecom.InCallService.VideoCall; 36 import android.telecom.PhoneAccount; 37 import android.telecom.PhoneAccountHandle; 38 import android.telecom.StatusHints; 39 import android.telecom.TelecomManager; 40 import android.telecom.VideoProfile; 41 import android.telephony.PhoneNumberUtils; 42 import android.text.TextUtils; 43 import com.android.contacts.common.compat.CallCompat; 44 import com.android.contacts.common.compat.TelephonyManagerCompat; 45 import com.android.contacts.common.compat.telecom.TelecomManagerCompat; 46 import com.android.dialer.callintent.CallInitiationType; 47 import com.android.dialer.callintent.CallIntentParser; 48 import com.android.dialer.callintent.CallSpecificAppData; 49 import com.android.dialer.common.Assert; 50 import com.android.dialer.common.LogUtil; 51 import com.android.dialer.configprovider.ConfigProviderBindings; 52 import com.android.dialer.enrichedcall.EnrichedCallCapabilities; 53 import com.android.dialer.enrichedcall.EnrichedCallComponent; 54 import com.android.dialer.enrichedcall.EnrichedCallManager; 55 import com.android.dialer.enrichedcall.EnrichedCallManager.CapabilitiesListener; 56 import com.android.dialer.enrichedcall.EnrichedCallManager.Filter; 57 import com.android.dialer.enrichedcall.EnrichedCallManager.StateChangedListener; 58 import com.android.dialer.enrichedcall.Session; 59 import com.android.dialer.lightbringer.LightbringerComponent; 60 import com.android.dialer.logging.ContactLookupResult; 61 import com.android.dialer.logging.DialerImpression; 62 import com.android.dialer.logging.Logger; 63 import com.android.dialer.theme.R; 64 import com.android.incallui.audiomode.AudioModeProvider; 65 import com.android.incallui.latencyreport.LatencyReport; 66 import com.android.incallui.util.TelecomCallUtil; 67 import com.android.incallui.videotech.VideoTech; 68 import com.android.incallui.videotech.VideoTech.VideoTechListener; 69 import com.android.incallui.videotech.empty.EmptyVideoTech; 70 import com.android.incallui.videotech.ims.ImsVideoTech; 71 import com.android.incallui.videotech.lightbringer.LightbringerTech; 72 import com.android.incallui.videotech.utils.VideoUtils; 73 import java.lang.annotation.Retention; 74 import java.lang.annotation.RetentionPolicy; 75 import java.util.ArrayList; 76 import java.util.List; 77 import java.util.Locale; 78 import java.util.Objects; 79 import java.util.UUID; 80 import java.util.concurrent.CopyOnWriteArrayList; 81 import java.util.concurrent.TimeUnit; 82 83 /** Describes a single call and its state. */ 84 public class DialerCall implements VideoTechListener, StateChangedListener, CapabilitiesListener { 85 86 public static final int CALL_HISTORY_STATUS_UNKNOWN = 0; 87 public static final int CALL_HISTORY_STATUS_PRESENT = 1; 88 public static final int CALL_HISTORY_STATUS_NOT_PRESENT = 2; 89 90 // Hard coded property for {@code Call}. Upstreamed change from Motorola. 91 // TODO(b/35359461): Move it to Telecom in framework. 92 public static final int PROPERTY_CODEC_KNOWN = 0x04000000; 93 94 private static final String ID_PREFIX = "DialerCall_"; 95 private static final String CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS = 96 "emergency_callback_window_millis"; 97 private static int sIdCounter = 0; 98 99 /** 100 * A counter used to append to restricted/private/hidden calls so that users can identify them in 101 * a conversation. This value is reset in {@link CallList#onCallRemoved(Context, Call)} when there 102 * are no live calls. 103 */ 104 private static int sHiddenCounter; 105 106 /** 107 * The unique call ID for every call. This will help us to identify each call and allow us the 108 * ability to stitch impressions to calls if needed. 109 */ 110 private final String uniqueCallId = UUID.randomUUID().toString(); 111 112 private final Call mTelecomCall; 113 private final LatencyReport mLatencyReport; 114 private final String mId; 115 private final int mHiddenId; 116 private final List<String> mChildCallIds = new ArrayList<>(); 117 private final LogState mLogState = new LogState(); 118 private final Context mContext; 119 private final DialerCallDelegate mDialerCallDelegate; 120 private final List<DialerCallListener> mListeners = new CopyOnWriteArrayList<>(); 121 private final List<CannedTextResponsesLoadedListener> mCannedTextResponsesLoadedListeners = 122 new CopyOnWriteArrayList<>(); 123 private final VideoTechManager mVideoTechManager; 124 125 private boolean mIsEmergencyCall; 126 private Uri mHandle; 127 private int mState = State.INVALID; 128 private DisconnectCause mDisconnectCause; 129 130 private boolean hasShownWiFiToLteHandoverToast; 131 private boolean doNotShowDialogForHandoffToWifiFailure; 132 133 private String mChildNumber; 134 private String mLastForwardedNumber; 135 private String mCallSubject; 136 private PhoneAccountHandle mPhoneAccountHandle; 137 @CallHistoryStatus private int mCallHistoryStatus = CALL_HISTORY_STATUS_UNKNOWN; 138 private boolean mIsSpam; 139 private boolean mIsBlocked; 140 private boolean isInUserSpamList; 141 private boolean isInUserWhiteList; 142 private boolean isInGlobalSpamList; 143 private boolean didShowCameraPermission; 144 private String callProviderLabel; 145 private String callbackNumber; 146 private int mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN; 147 private EnrichedCallCapabilities mEnrichedCallCapabilities; 148 private Session mEnrichedCallSession; 149 150 private int answerAndReleaseButtonDisplayedTimes = 0; 151 private boolean releasedByAnsweringSecondCall = false; 152 // Times when a second call is received but AnswerAndRelease button is not shown 153 // since it's not supported. 154 private int secondCallWithoutAnswerAndReleasedButtonTimes = 0; 155 getNumberFromHandle(Uri handle)156 public static String getNumberFromHandle(Uri handle) { 157 return handle == null ? "" : handle.getSchemeSpecificPart(); 158 } 159 160 /** 161 * Whether the call is put on hold by remote party. This is different than the {@link 162 * State#ONHOLD} state which indicates that the call is being held locally on the device. 163 */ 164 private boolean isRemotelyHeld; 165 166 /** Indicates whether this call is currently in the process of being merged into a conference. */ 167 private boolean isMergeInProcess; 168 169 /** 170 * Indicates whether the phone account associated with this call supports specifying a call 171 * subject. 172 */ 173 private boolean mIsCallSubjectSupported; 174 175 private final Call.Callback mTelecomCallCallback = 176 new Call.Callback() { 177 @Override 178 public void onStateChanged(Call call, int newState) { 179 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call + " newState=" + newState); 180 update(); 181 } 182 183 @Override 184 public void onParentChanged(Call call, Call newParent) { 185 LogUtil.v( 186 "TelecomCallCallback.onParentChanged", "call=" + call + " newParent=" + newParent); 187 update(); 188 } 189 190 @Override 191 public void onChildrenChanged(Call call, List<Call> children) { 192 update(); 193 } 194 195 @Override 196 public void onDetailsChanged(Call call, Call.Details details) { 197 LogUtil.v("TelecomCallCallback.onStateChanged", " call=" + call + " details=" + details); 198 update(); 199 } 200 201 @Override 202 public void onCannedTextResponsesLoaded(Call call, List<String> cannedTextResponses) { 203 LogUtil.v( 204 "TelecomCallCallback.onStateChanged", 205 "call=" + call + " cannedTextResponses=" + cannedTextResponses); 206 for (CannedTextResponsesLoadedListener listener : mCannedTextResponsesLoadedListeners) { 207 listener.onCannedTextResponsesLoaded(DialerCall.this); 208 } 209 } 210 211 @Override 212 public void onPostDialWait(Call call, String remainingPostDialSequence) { 213 LogUtil.v( 214 "TelecomCallCallback.onStateChanged", 215 "call=" + call + " remainingPostDialSequence=" + remainingPostDialSequence); 216 update(); 217 } 218 219 @Override 220 public void onVideoCallChanged(Call call, VideoCall videoCall) { 221 LogUtil.v( 222 "TelecomCallCallback.onStateChanged", "call=" + call + " videoCall=" + videoCall); 223 update(); 224 } 225 226 @Override 227 public void onCallDestroyed(Call call) { 228 LogUtil.v("TelecomCallCallback.onStateChanged", "call=" + call); 229 unregisterCallback(); 230 } 231 232 @Override 233 public void onConferenceableCallsChanged(Call call, List<Call> conferenceableCalls) { 234 LogUtil.v( 235 "DialerCall.onConferenceableCallsChanged", 236 "call %s, conferenceable calls: %d", 237 call, 238 conferenceableCalls.size()); 239 update(); 240 } 241 242 @Override 243 public void onConnectionEvent(android.telecom.Call call, String event, Bundle extras) { 244 LogUtil.v( 245 "DialerCall.onConnectionEvent", 246 "Call: " + call + ", Event: " + event + ", Extras: " + extras); 247 switch (event) { 248 // The Previous attempt to Merge two calls together has failed in Telecom. We must 249 // now update the UI to possibly re-enable the Merge button based on the number of 250 // currently conferenceable calls available or Connection Capabilities. 251 case android.telecom.Connection.EVENT_CALL_MERGE_FAILED: 252 update(); 253 break; 254 case TelephonyManagerCompat.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE: 255 notifyWiFiToLteHandover(); 256 break; 257 case TelephonyManagerCompat.EVENT_HANDOVER_TO_WIFI_FAILED: 258 notifyHandoverToWifiFailed(); 259 break; 260 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_HELD: 261 isRemotelyHeld = true; 262 update(); 263 break; 264 case TelephonyManagerCompat.EVENT_CALL_REMOTELY_UNHELD: 265 isRemotelyHeld = false; 266 update(); 267 break; 268 case TelephonyManagerCompat.EVENT_NOTIFY_INTERNATIONAL_CALL_ON_WFC: 269 notifyInternationalCallOnWifi(); 270 break; 271 case TelephonyManagerCompat.EVENT_MERGE_START: 272 LogUtil.i("DialerCall.onConnectionEvent", "merge start"); 273 isMergeInProcess = true; 274 break; 275 case TelephonyManagerCompat.EVENT_MERGE_COMPLETE: 276 LogUtil.i("DialerCall.onConnectionEvent", "merge complete"); 277 isMergeInProcess = false; 278 break; 279 default: 280 break; 281 } 282 } 283 }; 284 285 private long mTimeAddedMs; 286 DialerCall( Context context, DialerCallDelegate dialerCallDelegate, Call telecomCall, LatencyReport latencyReport, boolean registerCallback)287 public DialerCall( 288 Context context, 289 DialerCallDelegate dialerCallDelegate, 290 Call telecomCall, 291 LatencyReport latencyReport, 292 boolean registerCallback) { 293 Assert.isNotNull(context); 294 mContext = context; 295 mDialerCallDelegate = dialerCallDelegate; 296 mTelecomCall = telecomCall; 297 mLatencyReport = latencyReport; 298 mId = ID_PREFIX + Integer.toString(sIdCounter++); 299 300 // Must be after assigning mTelecomCall 301 mVideoTechManager = new VideoTechManager(this); 302 303 updateFromTelecomCall(); 304 if (isHiddenNumber() && TextUtils.isEmpty(getNumber())) { 305 mHiddenId = ++sHiddenCounter; 306 } else { 307 mHiddenId = 0; 308 } 309 310 if (registerCallback) { 311 mTelecomCall.registerCallback(mTelecomCallCallback); 312 } 313 314 mTimeAddedMs = System.currentTimeMillis(); 315 parseCallSpecificAppData(); 316 317 updateEnrichedCallSession(); 318 } 319 translateState(int state)320 private static int translateState(int state) { 321 switch (state) { 322 case Call.STATE_NEW: 323 case Call.STATE_CONNECTING: 324 return DialerCall.State.CONNECTING; 325 case Call.STATE_SELECT_PHONE_ACCOUNT: 326 return DialerCall.State.SELECT_PHONE_ACCOUNT; 327 case Call.STATE_DIALING: 328 return DialerCall.State.DIALING; 329 case Call.STATE_PULLING_CALL: 330 return DialerCall.State.PULLING; 331 case Call.STATE_RINGING: 332 return DialerCall.State.INCOMING; 333 case Call.STATE_ACTIVE: 334 return DialerCall.State.ACTIVE; 335 case Call.STATE_HOLDING: 336 return DialerCall.State.ONHOLD; 337 case Call.STATE_DISCONNECTED: 338 return DialerCall.State.DISCONNECTED; 339 case Call.STATE_DISCONNECTING: 340 return DialerCall.State.DISCONNECTING; 341 default: 342 return DialerCall.State.INVALID; 343 } 344 } 345 areSame(DialerCall call1, DialerCall call2)346 public static boolean areSame(DialerCall call1, DialerCall call2) { 347 if (call1 == null && call2 == null) { 348 return true; 349 } else if (call1 == null || call2 == null) { 350 return false; 351 } 352 353 // otherwise compare call Ids 354 return call1.getId().equals(call2.getId()); 355 } 356 areSameNumber(DialerCall call1, DialerCall call2)357 public static boolean areSameNumber(DialerCall call1, DialerCall call2) { 358 if (call1 == null && call2 == null) { 359 return true; 360 } else if (call1 == null || call2 == null) { 361 return false; 362 } 363 364 // otherwise compare call Numbers 365 return TextUtils.equals(call1.getNumber(), call2.getNumber()); 366 } 367 addListener(DialerCallListener listener)368 public void addListener(DialerCallListener listener) { 369 Assert.isMainThread(); 370 mListeners.add(listener); 371 } 372 removeListener(DialerCallListener listener)373 public void removeListener(DialerCallListener listener) { 374 Assert.isMainThread(); 375 mListeners.remove(listener); 376 } 377 addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener)378 public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) { 379 Assert.isMainThread(); 380 mCannedTextResponsesLoadedListeners.add(listener); 381 } 382 removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener)383 public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) { 384 Assert.isMainThread(); 385 mCannedTextResponsesLoadedListeners.remove(listener); 386 } 387 notifyWiFiToLteHandover()388 public void notifyWiFiToLteHandover() { 389 LogUtil.i("DialerCall.notifyWiFiToLteHandover", ""); 390 for (DialerCallListener listener : mListeners) { 391 listener.onWiFiToLteHandover(); 392 } 393 } 394 notifyHandoverToWifiFailed()395 public void notifyHandoverToWifiFailed() { 396 LogUtil.i("DialerCall.notifyHandoverToWifiFailed", ""); 397 for (DialerCallListener listener : mListeners) { 398 listener.onHandoverToWifiFailure(); 399 } 400 } 401 notifyInternationalCallOnWifi()402 public void notifyInternationalCallOnWifi() { 403 LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi"); 404 for (DialerCallListener dialerCallListener : mListeners) { 405 dialerCallListener.onInternationalCallOnWifi(); 406 } 407 } 408 getTelecomCall()409 /* package-private */ Call getTelecomCall() { 410 return mTelecomCall; 411 } 412 getStatusHints()413 public StatusHints getStatusHints() { 414 return mTelecomCall.getDetails().getStatusHints(); 415 } 416 getCameraDir()417 public int getCameraDir() { 418 return mCameraDirection; 419 } 420 setCameraDir(int cameraDir)421 public void setCameraDir(int cameraDir) { 422 if (cameraDir == CameraDirection.CAMERA_DIRECTION_FRONT_FACING 423 || cameraDir == CameraDirection.CAMERA_DIRECTION_BACK_FACING) { 424 mCameraDirection = cameraDir; 425 } else { 426 mCameraDirection = CameraDirection.CAMERA_DIRECTION_UNKNOWN; 427 } 428 } 429 update()430 private void update() { 431 Trace.beginSection("Update"); 432 int oldState = getState(); 433 // We want to potentially register a video call callback here. 434 updateFromTelecomCall(); 435 if (oldState != getState() && getState() == DialerCall.State.DISCONNECTED) { 436 for (DialerCallListener listener : mListeners) { 437 listener.onDialerCallDisconnect(); 438 } 439 EnrichedCallComponent.get(mContext) 440 .getEnrichedCallManager() 441 .unregisterCapabilitiesListener(this); 442 EnrichedCallComponent.get(mContext) 443 .getEnrichedCallManager() 444 .unregisterCapabilitiesListener(this); 445 } else { 446 for (DialerCallListener listener : mListeners) { 447 listener.onDialerCallUpdate(); 448 } 449 } 450 Trace.endSection(); 451 } 452 updateFromTelecomCall()453 private void updateFromTelecomCall() { 454 LogUtil.v("DialerCall.updateFromTelecomCall", mTelecomCall.toString()); 455 456 mVideoTechManager.dispatchCallStateChanged(mTelecomCall.getState()); 457 458 final int translatedState = translateState(mTelecomCall.getState()); 459 if (mState != State.BLOCKED) { 460 setState(translatedState); 461 setDisconnectCause(mTelecomCall.getDetails().getDisconnectCause()); 462 } 463 464 mChildCallIds.clear(); 465 final int numChildCalls = mTelecomCall.getChildren().size(); 466 for (int i = 0; i < numChildCalls; i++) { 467 mChildCallIds.add( 468 mDialerCallDelegate 469 .getDialerCallFromTelecomCall(mTelecomCall.getChildren().get(i)) 470 .getId()); 471 } 472 473 // The number of conferenced calls can change over the course of the call, so use the 474 // maximum number of conferenced child calls as the metric for conference call usage. 475 mLogState.conferencedCalls = Math.max(numChildCalls, mLogState.conferencedCalls); 476 477 updateFromCallExtras(mTelecomCall.getDetails().getExtras()); 478 479 // If the handle of the call has changed, update state for the call determining if it is an 480 // emergency call. 481 Uri newHandle = mTelecomCall.getDetails().getHandle(); 482 if (!Objects.equals(mHandle, newHandle)) { 483 mHandle = newHandle; 484 updateEmergencyCallState(); 485 } 486 487 // If the phone account handle of the call is set, cache capability bit indicating whether 488 // the phone account supports call subjects. 489 PhoneAccountHandle newPhoneAccountHandle = mTelecomCall.getDetails().getAccountHandle(); 490 if (!Objects.equals(mPhoneAccountHandle, newPhoneAccountHandle)) { 491 mPhoneAccountHandle = newPhoneAccountHandle; 492 493 if (mPhoneAccountHandle != null) { 494 PhoneAccount phoneAccount = 495 mContext.getSystemService(TelecomManager.class).getPhoneAccount(mPhoneAccountHandle); 496 if (phoneAccount != null) { 497 mIsCallSubjectSupported = 498 phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_CALL_SUBJECT); 499 } 500 } 501 } 502 } 503 504 /** 505 * Tests corruption of the {@code callExtras} bundle by calling {@link 506 * Bundle#containsKey(String)}. If the bundle is corrupted a {@link IllegalArgumentException} will 507 * be thrown and caught by this function. 508 * 509 * @param callExtras the bundle to verify 510 * @return {@code true} if the bundle is corrupted, {@code false} otherwise. 511 */ areCallExtrasCorrupted(Bundle callExtras)512 protected boolean areCallExtrasCorrupted(Bundle callExtras) { 513 /** 514 * There's currently a bug in Telephony service (b/25613098) that could corrupt the extras 515 * bundle, resulting in a IllegalArgumentException while validating data under {@link 516 * Bundle#containsKey(String)}. 517 */ 518 try { 519 callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS); 520 return false; 521 } catch (IllegalArgumentException e) { 522 LogUtil.e( 523 "DialerCall.areCallExtrasCorrupted", "callExtras is corrupted, ignoring exception", e); 524 return true; 525 } 526 } 527 updateFromCallExtras(Bundle callExtras)528 protected void updateFromCallExtras(Bundle callExtras) { 529 if (callExtras == null || areCallExtrasCorrupted(callExtras)) { 530 /** 531 * If the bundle is corrupted, abandon information update as a work around. These are not 532 * critical for the dialer to function. 533 */ 534 return; 535 } 536 // Check for a change in the child address and notify any listeners. 537 if (callExtras.containsKey(Connection.EXTRA_CHILD_ADDRESS)) { 538 String childNumber = callExtras.getString(Connection.EXTRA_CHILD_ADDRESS); 539 if (!Objects.equals(childNumber, mChildNumber)) { 540 mChildNumber = childNumber; 541 for (DialerCallListener listener : mListeners) { 542 listener.onDialerCallChildNumberChange(); 543 } 544 } 545 } 546 547 // Last forwarded number comes in as an array of strings. We want to choose the 548 // last item in the array. The forwarding numbers arrive independently of when the 549 // call is originally set up, so we need to notify the the UI of the change. 550 if (callExtras.containsKey(Connection.EXTRA_LAST_FORWARDED_NUMBER)) { 551 ArrayList<String> lastForwardedNumbers = 552 callExtras.getStringArrayList(Connection.EXTRA_LAST_FORWARDED_NUMBER); 553 554 if (lastForwardedNumbers != null) { 555 String lastForwardedNumber = null; 556 if (!lastForwardedNumbers.isEmpty()) { 557 lastForwardedNumber = lastForwardedNumbers.get(lastForwardedNumbers.size() - 1); 558 } 559 560 if (!Objects.equals(lastForwardedNumber, mLastForwardedNumber)) { 561 mLastForwardedNumber = lastForwardedNumber; 562 for (DialerCallListener listener : mListeners) { 563 listener.onDialerCallLastForwardedNumberChange(); 564 } 565 } 566 } 567 } 568 569 // DialerCall subject is present in the extras at the start of call, so we do not need to 570 // notify any other listeners of this. 571 if (callExtras.containsKey(Connection.EXTRA_CALL_SUBJECT)) { 572 String callSubject = callExtras.getString(Connection.EXTRA_CALL_SUBJECT); 573 if (!Objects.equals(mCallSubject, callSubject)) { 574 mCallSubject = callSubject; 575 } 576 } 577 } 578 getId()579 public String getId() { 580 return mId; 581 } 582 583 /** 584 * @return name appended with a number if the number is restricted/unknown and the user has 585 * received more than one restricted/unknown call. 586 */ 587 @Nullable updateNameIfRestricted(@ullable String name)588 public String updateNameIfRestricted(@Nullable String name) { 589 if (name != null && isHiddenNumber() && mHiddenId != 0 && sHiddenCounter > 1) { 590 return mContext.getString(R.string.unknown_counter, name, mHiddenId); 591 } 592 return name; 593 } 594 clearRestrictedCount()595 public static void clearRestrictedCount() { 596 sHiddenCounter = 0; 597 } 598 isHiddenNumber()599 private boolean isHiddenNumber() { 600 return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED 601 || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN; 602 } 603 hasShownWiFiToLteHandoverToast()604 public boolean hasShownWiFiToLteHandoverToast() { 605 return hasShownWiFiToLteHandoverToast; 606 } 607 setHasShownWiFiToLteHandoverToast()608 public void setHasShownWiFiToLteHandoverToast() { 609 hasShownWiFiToLteHandoverToast = true; 610 } 611 showWifiHandoverAlertAsToast()612 public boolean showWifiHandoverAlertAsToast() { 613 return doNotShowDialogForHandoffToWifiFailure; 614 } 615 setDoNotShowDialogForHandoffToWifiFailure(boolean bool)616 public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) { 617 doNotShowDialogForHandoffToWifiFailure = bool; 618 } 619 getTimeAddedMs()620 public long getTimeAddedMs() { 621 return mTimeAddedMs; 622 } 623 624 @Nullable getNumber()625 public String getNumber() { 626 return TelecomCallUtil.getNumber(mTelecomCall); 627 } 628 blockCall()629 public void blockCall() { 630 mTelecomCall.reject(false, null); 631 setState(State.BLOCKED); 632 } 633 634 @Nullable getHandle()635 public Uri getHandle() { 636 return mTelecomCall == null ? null : mTelecomCall.getDetails().getHandle(); 637 } 638 isEmergencyCall()639 public boolean isEmergencyCall() { 640 return mIsEmergencyCall; 641 } 642 isPotentialEmergencyCallback()643 public boolean isPotentialEmergencyCallback() { 644 // The property PROPERTY_EMERGENCY_CALLBACK_MODE is only set for CDMA calls when the system 645 // is actually in emergency callback mode (ie data is disabled). 646 if (hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE)) { 647 return true; 648 } 649 // We want to treat any incoming call that arrives a short time after an outgoing emergency call 650 // as a potential emergency callback. 651 if (getExtras() != null 652 && getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0) 653 > 0) { 654 long lastEmergencyCallMillis = 655 getExtras().getLong(TelecomManagerCompat.EXTRA_LAST_EMERGENCY_CALLBACK_TIME_MILLIS, 0); 656 if (isInEmergencyCallbackWindow(lastEmergencyCallMillis)) { 657 return true; 658 } 659 } 660 return false; 661 } 662 isInEmergencyCallbackWindow(long timestampMillis)663 boolean isInEmergencyCallbackWindow(long timestampMillis) { 664 long emergencyCallbackWindowMillis = 665 ConfigProviderBindings.get(mContext) 666 .getLong(CONFIG_EMERGENCY_CALLBACK_WINDOW_MILLIS, TimeUnit.MINUTES.toMillis(5)); 667 return System.currentTimeMillis() - timestampMillis < emergencyCallbackWindowMillis; 668 } 669 getState()670 public int getState() { 671 if (mTelecomCall != null && mTelecomCall.getParent() != null) { 672 return State.CONFERENCED; 673 } else { 674 return mState; 675 } 676 } 677 setState(int state)678 public void setState(int state) { 679 mState = state; 680 if (mState == State.INCOMING) { 681 mLogState.isIncoming = true; 682 } else if (mState == State.DISCONNECTED) { 683 mLogState.duration = 684 getConnectTimeMillis() == 0 ? 0 : System.currentTimeMillis() - getConnectTimeMillis(); 685 } 686 } 687 getNumberPresentation()688 public int getNumberPresentation() { 689 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getHandlePresentation(); 690 } 691 getCnapNamePresentation()692 public int getCnapNamePresentation() { 693 return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getCallerDisplayNamePresentation(); 694 } 695 696 @Nullable getCnapName()697 public String getCnapName() { 698 return mTelecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName(); 699 } 700 getIntentExtras()701 public Bundle getIntentExtras() { 702 return mTelecomCall.getDetails().getIntentExtras(); 703 } 704 705 @Nullable getExtras()706 public Bundle getExtras() { 707 return mTelecomCall == null ? null : mTelecomCall.getDetails().getExtras(); 708 } 709 710 /** @return The child number for the call, or {@code null} if none specified. */ getChildNumber()711 public String getChildNumber() { 712 return mChildNumber; 713 } 714 715 /** @return The last forwarded number for the call, or {@code null} if none specified. */ getLastForwardedNumber()716 public String getLastForwardedNumber() { 717 return mLastForwardedNumber; 718 } 719 720 /** @return The call subject, or {@code null} if none specified. */ getCallSubject()721 public String getCallSubject() { 722 return mCallSubject; 723 } 724 725 /** 726 * @return {@code true} if the call's phone account supports call subjects, {@code false} 727 * otherwise. 728 */ isCallSubjectSupported()729 public boolean isCallSubjectSupported() { 730 return mIsCallSubjectSupported; 731 } 732 733 /** Returns call disconnect cause, defined by {@link DisconnectCause}. */ getDisconnectCause()734 public DisconnectCause getDisconnectCause() { 735 if (mState == State.DISCONNECTED || mState == State.IDLE) { 736 return mDisconnectCause; 737 } 738 739 return new DisconnectCause(DisconnectCause.UNKNOWN); 740 } 741 setDisconnectCause(DisconnectCause disconnectCause)742 public void setDisconnectCause(DisconnectCause disconnectCause) { 743 mDisconnectCause = disconnectCause; 744 mLogState.disconnectCause = mDisconnectCause; 745 } 746 747 /** Returns the possible text message responses. */ getCannedSmsResponses()748 public List<String> getCannedSmsResponses() { 749 return mTelecomCall.getCannedTextResponses(); 750 } 751 752 /** Checks if the call supports the given set of capabilities supplied as a bit mask. */ can(int capabilities)753 public boolean can(int capabilities) { 754 int supportedCapabilities = mTelecomCall.getDetails().getCallCapabilities(); 755 756 if ((capabilities & Call.Details.CAPABILITY_MERGE_CONFERENCE) != 0) { 757 // We allow you to merge if the capabilities allow it or if it is a call with 758 // conferenceable calls. 759 if (mTelecomCall.getConferenceableCalls().isEmpty() 760 && ((Call.Details.CAPABILITY_MERGE_CONFERENCE & supportedCapabilities) == 0)) { 761 // Cannot merge calls if there are no calls to merge with. 762 return false; 763 } 764 capabilities &= ~Call.Details.CAPABILITY_MERGE_CONFERENCE; 765 } 766 return (capabilities == (capabilities & supportedCapabilities)); 767 } 768 hasProperty(int property)769 public boolean hasProperty(int property) { 770 return mTelecomCall.getDetails().hasProperty(property); 771 } 772 773 @NonNull getUniqueCallId()774 public String getUniqueCallId() { 775 return uniqueCallId; 776 } 777 778 /** Gets the time when the call first became active. */ getConnectTimeMillis()779 public long getConnectTimeMillis() { 780 return mTelecomCall.getDetails().getConnectTimeMillis(); 781 } 782 isConferenceCall()783 public boolean isConferenceCall() { 784 return hasProperty(Call.Details.PROPERTY_CONFERENCE); 785 } 786 787 @Nullable getGatewayInfo()788 public GatewayInfo getGatewayInfo() { 789 return mTelecomCall == null ? null : mTelecomCall.getDetails().getGatewayInfo(); 790 } 791 792 @Nullable getAccountHandle()793 public PhoneAccountHandle getAccountHandle() { 794 return mTelecomCall == null ? null : mTelecomCall.getDetails().getAccountHandle(); 795 } 796 797 /** @return The {@link VideoCall} instance associated with the {@link Call}. */ getVideoCall()798 public VideoCall getVideoCall() { 799 return mTelecomCall == null ? null : mTelecomCall.getVideoCall(); 800 } 801 getChildCallIds()802 public List<String> getChildCallIds() { 803 return mChildCallIds; 804 } 805 getParentId()806 public String getParentId() { 807 Call parentCall = mTelecomCall.getParent(); 808 if (parentCall != null) { 809 return mDialerCallDelegate.getDialerCallFromTelecomCall(parentCall).getId(); 810 } 811 return null; 812 } 813 getVideoState()814 public int getVideoState() { 815 return mTelecomCall.getDetails().getVideoState(); 816 } 817 isVideoCall()818 public boolean isVideoCall() { 819 return getVideoTech().isTransmittingOrReceiving(); 820 } 821 hasReceivedVideoUpgradeRequest()822 public boolean hasReceivedVideoUpgradeRequest() { 823 return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState()); 824 } 825 hasSentVideoUpgradeRequest()826 public boolean hasSentVideoUpgradeRequest() { 827 return VideoUtils.hasSentVideoUpgradeRequest(getVideoTech().getSessionModificationState()); 828 } 829 830 /** 831 * Determines if the call handle is an emergency number or not and caches the result to avoid 832 * repeated calls to isEmergencyNumber. 833 */ updateEmergencyCallState()834 private void updateEmergencyCallState() { 835 mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall); 836 } 837 getLogState()838 public LogState getLogState() { 839 return mLogState; 840 } 841 842 /** 843 * Determines if the call is an external call. 844 * 845 * <p>An external call is one which does not exist locally for the {@link 846 * android.telecom.ConnectionService} it is associated with. 847 * 848 * <p>External calls are only supported in N and higher. 849 * 850 * @return {@code true} if the call is an external call, {@code false} otherwise. 851 */ isExternalCall()852 public boolean isExternalCall() { 853 return VERSION.SDK_INT >= VERSION_CODES.N 854 && hasProperty(CallCompat.Details.PROPERTY_IS_EXTERNAL_CALL); 855 } 856 857 /** 858 * Determines if answering this call will cause an ongoing video call to be dropped. 859 * 860 * @return {@code true} if answering this call will drop an ongoing video call, {@code false} 861 * otherwise. 862 */ answeringDisconnectsForegroundVideoCall()863 public boolean answeringDisconnectsForegroundVideoCall() { 864 Bundle extras = getExtras(); 865 if (extras == null 866 || !extras.containsKey(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL)) { 867 return false; 868 } 869 return extras.getBoolean(CallCompat.Details.EXTRA_ANSWERING_DROPS_FOREGROUND_CALL); 870 } 871 parseCallSpecificAppData()872 private void parseCallSpecificAppData() { 873 if (isExternalCall()) { 874 return; 875 } 876 877 mLogState.callSpecificAppData = CallIntentParser.getCallSpecificAppData(getIntentExtras()); 878 if (mLogState.callSpecificAppData == null) { 879 880 mLogState.callSpecificAppData = 881 CallSpecificAppData.newBuilder() 882 .setCallInitiationType(CallInitiationType.Type.EXTERNAL_INITIATION) 883 .build(); 884 } 885 if (getState() == State.INCOMING) { 886 mLogState.callSpecificAppData = 887 mLogState 888 .callSpecificAppData 889 .toBuilder() 890 .setCallInitiationType(CallInitiationType.Type.INCOMING_INITIATION) 891 .build(); 892 } 893 } 894 895 @Override toString()896 public String toString() { 897 if (mTelecomCall == null) { 898 // This should happen only in testing since otherwise we would never have a null 899 // Telecom call. 900 return String.valueOf(mId); 901 } 902 903 return String.format( 904 Locale.US, 905 "[%s, %s, %s, %s, children:%s, parent:%s, " 906 + "conferenceable:%s, videoState:%s, mSessionModificationState:%d, CameraDir:%s]", 907 mId, 908 State.toString(getState()), 909 Details.capabilitiesToString(mTelecomCall.getDetails().getCallCapabilities()), 910 Details.propertiesToString(mTelecomCall.getDetails().getCallProperties()), 911 mChildCallIds, 912 getParentId(), 913 this.mTelecomCall.getConferenceableCalls(), 914 VideoProfile.videoStateToString(mTelecomCall.getDetails().getVideoState()), 915 getVideoTech().getSessionModificationState(), 916 getCameraDir()); 917 } 918 toSimpleString()919 public String toSimpleString() { 920 return super.toString(); 921 } 922 923 @CallHistoryStatus getCallHistoryStatus()924 public int getCallHistoryStatus() { 925 return mCallHistoryStatus; 926 } 927 setCallHistoryStatus(@allHistoryStatus int callHistoryStatus)928 public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) { 929 mCallHistoryStatus = callHistoryStatus; 930 } 931 didShowCameraPermission()932 public boolean didShowCameraPermission() { 933 return didShowCameraPermission; 934 } 935 setDidShowCameraPermission(boolean didShow)936 public void setDidShowCameraPermission(boolean didShow) { 937 didShowCameraPermission = didShow; 938 } 939 isInGlobalSpamList()940 public boolean isInGlobalSpamList() { 941 return isInGlobalSpamList; 942 } 943 setIsInGlobalSpamList(boolean inSpamList)944 public void setIsInGlobalSpamList(boolean inSpamList) { 945 isInGlobalSpamList = inSpamList; 946 } 947 isInUserSpamList()948 public boolean isInUserSpamList() { 949 return isInUserSpamList; 950 } 951 setIsInUserSpamList(boolean inSpamList)952 public void setIsInUserSpamList(boolean inSpamList) { 953 isInUserSpamList = inSpamList; 954 } 955 isInUserWhiteList()956 public boolean isInUserWhiteList() { 957 return isInUserWhiteList; 958 } 959 setIsInUserWhiteList(boolean inWhiteList)960 public void setIsInUserWhiteList(boolean inWhiteList) { 961 isInUserWhiteList = inWhiteList; 962 } 963 isSpam()964 public boolean isSpam() { 965 return mIsSpam; 966 } 967 setSpam(boolean isSpam)968 public void setSpam(boolean isSpam) { 969 mIsSpam = isSpam; 970 } 971 isBlocked()972 public boolean isBlocked() { 973 return mIsBlocked; 974 } 975 setBlockedStatus(boolean isBlocked)976 public void setBlockedStatus(boolean isBlocked) { 977 mIsBlocked = isBlocked; 978 } 979 isRemotelyHeld()980 public boolean isRemotelyHeld() { 981 return isRemotelyHeld; 982 } 983 isMergeInProcess()984 public boolean isMergeInProcess() { 985 return isMergeInProcess; 986 } 987 isIncoming()988 public boolean isIncoming() { 989 return mLogState.isIncoming; 990 } 991 getLatencyReport()992 public LatencyReport getLatencyReport() { 993 return mLatencyReport; 994 } 995 getAnswerAndReleaseButtonDisplayedTimes()996 public int getAnswerAndReleaseButtonDisplayedTimes() { 997 return answerAndReleaseButtonDisplayedTimes; 998 } 999 increaseAnswerAndReleaseButtonDisplayedTimes()1000 public void increaseAnswerAndReleaseButtonDisplayedTimes() { 1001 answerAndReleaseButtonDisplayedTimes++; 1002 } 1003 getReleasedByAnsweringSecondCall()1004 public boolean getReleasedByAnsweringSecondCall() { 1005 return releasedByAnsweringSecondCall; 1006 } 1007 setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall)1008 public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) { 1009 this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall; 1010 } 1011 getSecondCallWithoutAnswerAndReleasedButtonTimes()1012 public int getSecondCallWithoutAnswerAndReleasedButtonTimes() { 1013 return secondCallWithoutAnswerAndReleasedButtonTimes; 1014 } 1015 increaseSecondCallWithoutAnswerAndReleasedButtonTimes()1016 public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() { 1017 secondCallWithoutAnswerAndReleasedButtonTimes++; 1018 } 1019 1020 @Nullable getEnrichedCallCapabilities()1021 public EnrichedCallCapabilities getEnrichedCallCapabilities() { 1022 return mEnrichedCallCapabilities; 1023 } 1024 setEnrichedCallCapabilities( @ullable EnrichedCallCapabilities mEnrichedCallCapabilities)1025 public void setEnrichedCallCapabilities( 1026 @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) { 1027 this.mEnrichedCallCapabilities = mEnrichedCallCapabilities; 1028 } 1029 1030 @Nullable getEnrichedCallSession()1031 public Session getEnrichedCallSession() { 1032 return mEnrichedCallSession; 1033 } 1034 setEnrichedCallSession(@ullable Session mEnrichedCallSession)1035 public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) { 1036 this.mEnrichedCallSession = mEnrichedCallSession; 1037 } 1038 unregisterCallback()1039 public void unregisterCallback() { 1040 mTelecomCall.unregisterCallback(mTelecomCallCallback); 1041 } 1042 phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault)1043 public void phoneAccountSelected(PhoneAccountHandle accountHandle, boolean setDefault) { 1044 LogUtil.i( 1045 "DialerCall.phoneAccountSelected", 1046 "accountHandle: %s, setDefault: %b", 1047 accountHandle, 1048 setDefault); 1049 mTelecomCall.phoneAccountSelected(accountHandle, setDefault); 1050 } 1051 disconnect()1052 public void disconnect() { 1053 LogUtil.i("DialerCall.disconnect", ""); 1054 setState(DialerCall.State.DISCONNECTING); 1055 for (DialerCallListener listener : mListeners) { 1056 listener.onDialerCallUpdate(); 1057 } 1058 mTelecomCall.disconnect(); 1059 } 1060 hold()1061 public void hold() { 1062 LogUtil.i("DialerCall.hold", ""); 1063 mTelecomCall.hold(); 1064 } 1065 unhold()1066 public void unhold() { 1067 LogUtil.i("DialerCall.unhold", ""); 1068 mTelecomCall.unhold(); 1069 } 1070 splitFromConference()1071 public void splitFromConference() { 1072 LogUtil.i("DialerCall.splitFromConference", ""); 1073 mTelecomCall.splitFromConference(); 1074 } 1075 answer(int videoState)1076 public void answer(int videoState) { 1077 LogUtil.i("DialerCall.answer", "videoState: " + videoState); 1078 mTelecomCall.answer(videoState); 1079 } 1080 answer()1081 public void answer() { 1082 answer(mTelecomCall.getDetails().getVideoState()); 1083 } 1084 reject(boolean rejectWithMessage, String message)1085 public void reject(boolean rejectWithMessage, String message) { 1086 LogUtil.i("DialerCall.reject", ""); 1087 mTelecomCall.reject(rejectWithMessage, message); 1088 } 1089 1090 /** Return the string label to represent the call provider */ getCallProviderLabel()1091 public String getCallProviderLabel() { 1092 if (callProviderLabel == null) { 1093 PhoneAccount account = getPhoneAccount(); 1094 if (account != null && !TextUtils.isEmpty(account.getLabel())) { 1095 List<PhoneAccountHandle> accounts = 1096 mContext.getSystemService(TelecomManager.class).getCallCapablePhoneAccounts(); 1097 if (accounts != null && accounts.size() > 1) { 1098 callProviderLabel = account.getLabel().toString(); 1099 } 1100 } 1101 if (callProviderLabel == null) { 1102 callProviderLabel = ""; 1103 } 1104 } 1105 return callProviderLabel; 1106 } 1107 getPhoneAccount()1108 private PhoneAccount getPhoneAccount() { 1109 PhoneAccountHandle accountHandle = getAccountHandle(); 1110 if (accountHandle == null) { 1111 return null; 1112 } 1113 return mContext.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle); 1114 } 1115 getVideoTech()1116 public VideoTech getVideoTech() { 1117 return mVideoTechManager.getVideoTech(); 1118 } 1119 getCallbackNumber()1120 public String getCallbackNumber() { 1121 if (callbackNumber == null) { 1122 // Show the emergency callback number if either: 1123 // 1. This is an emergency call. 1124 // 2. The phone is in Emergency Callback Mode, which means we should show the callback 1125 // number. 1126 boolean showCallbackNumber = hasProperty(Details.PROPERTY_EMERGENCY_CALLBACK_MODE); 1127 1128 if (isEmergencyCall() || showCallbackNumber) { 1129 callbackNumber = getSubscriptionNumber(); 1130 } else { 1131 StatusHints statusHints = getTelecomCall().getDetails().getStatusHints(); 1132 if (statusHints != null) { 1133 Bundle extras = statusHints.getExtras(); 1134 if (extras != null) { 1135 callbackNumber = extras.getString(TelecomManager.EXTRA_CALL_BACK_NUMBER); 1136 } 1137 } 1138 } 1139 1140 String simNumber = 1141 mContext.getSystemService(TelecomManager.class).getLine1Number(getAccountHandle()); 1142 if (!showCallbackNumber && PhoneNumberUtils.compare(callbackNumber, simNumber)) { 1143 LogUtil.v( 1144 "DialerCall.getCallbackNumber", 1145 "numbers are the same (and callback number is not being forced to show);" 1146 + " not showing the callback number"); 1147 callbackNumber = ""; 1148 } 1149 if (callbackNumber == null) { 1150 callbackNumber = ""; 1151 } 1152 } 1153 return callbackNumber; 1154 } 1155 getSubscriptionNumber()1156 private String getSubscriptionNumber() { 1157 // If it's an emergency call, and they're not populating the callback number, 1158 // then try to fall back to the phone sub info (to hopefully get the SIM's 1159 // number directly from the telephony layer). 1160 PhoneAccountHandle accountHandle = getAccountHandle(); 1161 if (accountHandle != null) { 1162 PhoneAccount account = 1163 mContext.getSystemService(TelecomManager.class).getPhoneAccount(accountHandle); 1164 if (account != null) { 1165 return getNumberFromHandle(account.getSubscriptionAddress()); 1166 } 1167 } 1168 return null; 1169 } 1170 1171 @Override onVideoTechStateChanged()1172 public void onVideoTechStateChanged() { 1173 update(); 1174 } 1175 1176 @Override onSessionModificationStateChanged()1177 public void onSessionModificationStateChanged() { 1178 for (DialerCallListener listener : mListeners) { 1179 listener.onDialerCallSessionModificationStateChange(); 1180 } 1181 } 1182 1183 @Override onCameraDimensionsChanged(int width, int height)1184 public void onCameraDimensionsChanged(int width, int height) { 1185 InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height); 1186 } 1187 1188 @Override onPeerDimensionsChanged(int width, int height)1189 public void onPeerDimensionsChanged(int width, int height) { 1190 InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height); 1191 } 1192 1193 @Override onVideoUpgradeRequestReceived()1194 public void onVideoUpgradeRequestReceived() { 1195 LogUtil.enterBlock("DialerCall.onVideoUpgradeRequestReceived"); 1196 1197 for (DialerCallListener listener : mListeners) { 1198 listener.onDialerCallUpgradeToVideo(); 1199 } 1200 1201 update(); 1202 1203 Logger.get(mContext) 1204 .logCallImpression( 1205 DialerImpression.Type.VIDEO_CALL_REQUEST_RECEIVED, getUniqueCallId(), getTimeAddedMs()); 1206 } 1207 1208 @Override onUpgradedToVideo(boolean switchToSpeaker)1209 public void onUpgradedToVideo(boolean switchToSpeaker) { 1210 LogUtil.enterBlock("DialerCall.onUpgradedToVideo"); 1211 1212 if (!switchToSpeaker) { 1213 return; 1214 } 1215 1216 CallAudioState audioState = AudioModeProvider.getInstance().getAudioState(); 1217 1218 if (0 != (CallAudioState.ROUTE_BLUETOOTH & audioState.getSupportedRouteMask())) { 1219 LogUtil.e( 1220 "DialerCall.onUpgradedToVideo", 1221 "toggling speakerphone not allowed when bluetooth supported."); 1222 return; 1223 } 1224 1225 if (audioState.getRoute() == CallAudioState.ROUTE_SPEAKER) { 1226 return; 1227 } 1228 1229 TelecomAdapter.getInstance().setAudioRoute(CallAudioState.ROUTE_SPEAKER); 1230 } 1231 1232 @Override onCapabilitiesUpdated()1233 public void onCapabilitiesUpdated() { 1234 if (getNumber() == null) { 1235 return; 1236 } 1237 EnrichedCallCapabilities capabilities = 1238 EnrichedCallComponent.get(mContext).getEnrichedCallManager().getCapabilities(getNumber()); 1239 if (capabilities != null) { 1240 setEnrichedCallCapabilities(capabilities); 1241 update(); 1242 } 1243 } 1244 1245 @Override onEnrichedCallStateChanged()1246 public void onEnrichedCallStateChanged() { 1247 updateEnrichedCallSession(); 1248 } 1249 updateEnrichedCallSession()1250 private void updateEnrichedCallSession() { 1251 if (getNumber() == null) { 1252 return; 1253 } 1254 if (getEnrichedCallSession() != null) { 1255 // State changes to existing sessions are currently handled by the UI components (which have 1256 // their own listeners). Someday instead we could remove those and just call update() here and 1257 // have the usual onDialerCallUpdate update the UI. 1258 dispatchOnEnrichedCallSessionUpdate(); 1259 return; 1260 } 1261 1262 EnrichedCallManager manager = EnrichedCallComponent.get(mContext).getEnrichedCallManager(); 1263 1264 Filter filter = 1265 isIncoming() 1266 ? manager.createIncomingCallComposerFilter() 1267 : manager.createOutgoingCallComposerFilter(); 1268 1269 Session session = manager.getSession(getUniqueCallId(), getNumber(), filter); 1270 if (session == null) { 1271 return; 1272 } 1273 1274 session.setUniqueDialerCallId(getUniqueCallId()); 1275 setEnrichedCallSession(session); 1276 1277 LogUtil.i( 1278 "DialerCall.updateEnrichedCallSession", 1279 "setting session %d's dialer id to %s", 1280 session.getSessionId(), 1281 getUniqueCallId()); 1282 1283 dispatchOnEnrichedCallSessionUpdate(); 1284 } 1285 dispatchOnEnrichedCallSessionUpdate()1286 private void dispatchOnEnrichedCallSessionUpdate() { 1287 for (DialerCallListener listener : mListeners) { 1288 listener.onEnrichedCallSessionUpdate(); 1289 } 1290 } 1291 onRemovedFromCallList()1292 void onRemovedFromCallList() { 1293 // Ensure we clean up when this call is removed. 1294 mVideoTechManager.dispatchRemovedFromCallList(); 1295 } 1296 1297 /** 1298 * Specifies whether a number is in the call history or not. {@link #CALL_HISTORY_STATUS_UNKNOWN} 1299 * means there is no result. 1300 */ 1301 @IntDef({ 1302 CALL_HISTORY_STATUS_UNKNOWN, 1303 CALL_HISTORY_STATUS_PRESENT, 1304 CALL_HISTORY_STATUS_NOT_PRESENT 1305 }) 1306 @Retention(RetentionPolicy.SOURCE) 1307 public @interface CallHistoryStatus {} 1308 1309 /* Defines different states of this call */ 1310 public static class State { 1311 1312 public static final int INVALID = 0; 1313 public static final int NEW = 1; /* The call is new. */ 1314 public static final int IDLE = 2; /* The call is idle. Nothing active */ 1315 public static final int ACTIVE = 3; /* There is an active call */ 1316 public static final int INCOMING = 4; /* A normal incoming phone call */ 1317 public static final int CALL_WAITING = 5; /* Incoming call while another is active */ 1318 public static final int DIALING = 6; /* An outgoing call during dial phase */ 1319 public static final int REDIALING = 7; /* Subsequent dialing attempt after a failure */ 1320 public static final int ONHOLD = 8; /* An active phone call placed on hold */ 1321 public static final int DISCONNECTING = 9; /* A call is being ended. */ 1322 public static final int DISCONNECTED = 10; /* State after a call disconnects */ 1323 public static final int CONFERENCED = 11; /* DialerCall part of a conference call */ 1324 public static final int SELECT_PHONE_ACCOUNT = 12; /* Waiting for account selection */ 1325 public static final int CONNECTING = 13; /* Waiting for Telecom broadcast to finish */ 1326 public static final int BLOCKED = 14; /* The number was found on the block list */ 1327 public static final int PULLING = 15; /* An external call being pulled to the device */ 1328 isConnectingOrConnected(int state)1329 public static boolean isConnectingOrConnected(int state) { 1330 switch (state) { 1331 case ACTIVE: 1332 case INCOMING: 1333 case CALL_WAITING: 1334 case CONNECTING: 1335 case DIALING: 1336 case PULLING: 1337 case REDIALING: 1338 case ONHOLD: 1339 case CONFERENCED: 1340 return true; 1341 default: 1342 return false; 1343 } 1344 } 1345 isDialing(int state)1346 public static boolean isDialing(int state) { 1347 return state == DIALING || state == PULLING || state == REDIALING; 1348 } 1349 toString(int state)1350 public static String toString(int state) { 1351 switch (state) { 1352 case INVALID: 1353 return "INVALID"; 1354 case NEW: 1355 return "NEW"; 1356 case IDLE: 1357 return "IDLE"; 1358 case ACTIVE: 1359 return "ACTIVE"; 1360 case INCOMING: 1361 return "INCOMING"; 1362 case CALL_WAITING: 1363 return "CALL_WAITING"; 1364 case DIALING: 1365 return "DIALING"; 1366 case PULLING: 1367 return "PULLING"; 1368 case REDIALING: 1369 return "REDIALING"; 1370 case ONHOLD: 1371 return "ONHOLD"; 1372 case DISCONNECTING: 1373 return "DISCONNECTING"; 1374 case DISCONNECTED: 1375 return "DISCONNECTED"; 1376 case CONFERENCED: 1377 return "CONFERENCED"; 1378 case SELECT_PHONE_ACCOUNT: 1379 return "SELECT_PHONE_ACCOUNT"; 1380 case CONNECTING: 1381 return "CONNECTING"; 1382 case BLOCKED: 1383 return "BLOCKED"; 1384 default: 1385 return "UNKNOWN"; 1386 } 1387 } 1388 } 1389 1390 /** Camera direction constants */ 1391 public static class CameraDirection { 1392 public static final int CAMERA_DIRECTION_UNKNOWN = -1; 1393 public static final int CAMERA_DIRECTION_FRONT_FACING = CameraCharacteristics.LENS_FACING_FRONT; 1394 public static final int CAMERA_DIRECTION_BACK_FACING = CameraCharacteristics.LENS_FACING_BACK; 1395 } 1396 1397 /** 1398 * Tracks any state variables that is useful for logging. There is some amount of overlap with 1399 * existing call member variables, but this duplication helps to ensure that none of these logging 1400 * variables will interface with/and affect call logic. 1401 */ 1402 public static class LogState { 1403 1404 public DisconnectCause disconnectCause; 1405 public boolean isIncoming = false; 1406 public ContactLookupResult.Type contactLookupResult = 1407 ContactLookupResult.Type.UNKNOWN_LOOKUP_RESULT_TYPE; 1408 public CallSpecificAppData callSpecificAppData; 1409 // If this was a conference call, the total number of calls involved in the conference. 1410 public int conferencedCalls = 0; 1411 public long duration = 0; 1412 public boolean isLogged = false; 1413 lookupToString(ContactLookupResult.Type lookupType)1414 private static String lookupToString(ContactLookupResult.Type lookupType) { 1415 switch (lookupType) { 1416 case LOCAL_CONTACT: 1417 return "Local"; 1418 case LOCAL_CACHE: 1419 return "Cache"; 1420 case REMOTE: 1421 return "Remote"; 1422 case EMERGENCY: 1423 return "Emergency"; 1424 case VOICEMAIL: 1425 return "Voicemail"; 1426 default: 1427 return "Not found"; 1428 } 1429 } 1430 initiationToString(CallSpecificAppData callSpecificAppData)1431 private static String initiationToString(CallSpecificAppData callSpecificAppData) { 1432 if (callSpecificAppData == null) { 1433 return "null"; 1434 } 1435 switch (callSpecificAppData.getCallInitiationType()) { 1436 case INCOMING_INITIATION: 1437 return "Incoming"; 1438 case DIALPAD: 1439 return "Dialpad"; 1440 case SPEED_DIAL: 1441 return "Speed Dial"; 1442 case REMOTE_DIRECTORY: 1443 return "Remote Directory"; 1444 case SMART_DIAL: 1445 return "Smart Dial"; 1446 case REGULAR_SEARCH: 1447 return "Regular Search"; 1448 case CALL_LOG: 1449 return "DialerCall Log"; 1450 case CALL_LOG_FILTER: 1451 return "DialerCall Log Filter"; 1452 case VOICEMAIL_LOG: 1453 return "Voicemail Log"; 1454 case CALL_DETAILS: 1455 return "DialerCall Details"; 1456 case QUICK_CONTACTS: 1457 return "Quick Contacts"; 1458 case EXTERNAL_INITIATION: 1459 return "External"; 1460 case LAUNCHER_SHORTCUT: 1461 return "Launcher Shortcut"; 1462 default: 1463 return "Unknown: " + callSpecificAppData.getCallInitiationType(); 1464 } 1465 } 1466 1467 @Override toString()1468 public String toString() { 1469 return String.format( 1470 Locale.US, 1471 "[" 1472 + "%s, " // DisconnectCause toString already describes the object type 1473 + "isIncoming: %s, " 1474 + "contactLookup: %s, " 1475 + "callInitiation: %s, " 1476 + "duration: %s" 1477 + "]", 1478 disconnectCause, 1479 isIncoming, 1480 lookupToString(contactLookupResult), 1481 initiationToString(callSpecificAppData), 1482 duration); 1483 } 1484 } 1485 1486 private static class VideoTechManager { 1487 private final Context context; 1488 private final EmptyVideoTech emptyVideoTech = new EmptyVideoTech(); 1489 private final List<VideoTech> videoTechs; 1490 private VideoTech savedTech; 1491 VideoTechManager(DialerCall call)1492 VideoTechManager(DialerCall call) { 1493 this.context = call.mContext; 1494 1495 String phoneNumber = call.getNumber(); 1496 phoneNumber = phoneNumber != null ? phoneNumber : ""; 1497 phoneNumber = phoneNumber.replaceAll("[^+0-9]", ""); 1498 1499 // Insert order here determines the priority of that video tech option 1500 videoTechs = new ArrayList<>(); 1501 videoTechs.add(new ImsVideoTech(Logger.get(call.mContext), call, call.mTelecomCall)); 1502 1503 VideoTech rcsVideoTech = 1504 EnrichedCallComponent.get(call.mContext) 1505 .getRcsVideoShareFactory() 1506 .newRcsVideoShare( 1507 EnrichedCallComponent.get(call.mContext).getEnrichedCallManager(), 1508 call, 1509 phoneNumber); 1510 if (rcsVideoTech != null) { 1511 videoTechs.add(rcsVideoTech); 1512 } 1513 1514 videoTechs.add( 1515 new LightbringerTech( 1516 LightbringerComponent.get(call.mContext).getLightbringer(), 1517 call, 1518 call.mTelecomCall, 1519 phoneNumber)); 1520 } 1521 getVideoTech()1522 VideoTech getVideoTech() { 1523 if (savedTech != null) { 1524 return savedTech; 1525 } 1526 1527 for (VideoTech tech : videoTechs) { 1528 if (tech.isAvailable(context)) { 1529 // Remember the first VideoTech that becomes available and always use it 1530 savedTech = tech; 1531 return savedTech; 1532 } 1533 } 1534 1535 return emptyVideoTech; 1536 } 1537 dispatchCallStateChanged(int newState)1538 void dispatchCallStateChanged(int newState) { 1539 for (VideoTech videoTech : videoTechs) { 1540 videoTech.onCallStateChanged(context, newState); 1541 } 1542 } 1543 dispatchRemovedFromCallList()1544 void dispatchRemovedFromCallList() { 1545 for (VideoTech videoTech : videoTechs) { 1546 videoTech.onRemovedFromCallList(); 1547 } 1548 } 1549 } 1550 1551 /** Called when canned text responses have been loaded. */ 1552 public interface CannedTextResponsesLoadedListener { onCannedTextResponsesLoaded(DialerCall call)1553 void onCannedTextResponsesLoaded(DialerCall call); 1554 } 1555 } 1556