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