Home | History | Annotate | Download | only in call
      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 
    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 
    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 
    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 
    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 
    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 
    368   public void addListener(DialerCallListener listener) {
    369     Assert.isMainThread();
    370     mListeners.add(listener);
    371   }
    372 
    373   public void removeListener(DialerCallListener listener) {
    374     Assert.isMainThread();
    375     mListeners.remove(listener);
    376   }
    377 
    378   public void addCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
    379     Assert.isMainThread();
    380     mCannedTextResponsesLoadedListeners.add(listener);
    381   }
    382 
    383   public void removeCannedTextResponsesLoadedListener(CannedTextResponsesLoadedListener listener) {
    384     Assert.isMainThread();
    385     mCannedTextResponsesLoadedListeners.remove(listener);
    386   }
    387 
    388   public void notifyWiFiToLteHandover() {
    389     LogUtil.i("DialerCall.notifyWiFiToLteHandover", "");
    390     for (DialerCallListener listener : mListeners) {
    391       listener.onWiFiToLteHandover();
    392     }
    393   }
    394 
    395   public void notifyHandoverToWifiFailed() {
    396     LogUtil.i("DialerCall.notifyHandoverToWifiFailed", "");
    397     for (DialerCallListener listener : mListeners) {
    398       listener.onHandoverToWifiFailure();
    399     }
    400   }
    401 
    402   public void notifyInternationalCallOnWifi() {
    403     LogUtil.enterBlock("DialerCall.notifyInternationalCallOnWifi");
    404     for (DialerCallListener dialerCallListener : mListeners) {
    405       dialerCallListener.onInternationalCallOnWifi();
    406     }
    407   }
    408 
    409   /* package-private */ Call getTelecomCall() {
    410     return mTelecomCall;
    411   }
    412 
    413   public StatusHints getStatusHints() {
    414     return mTelecomCall.getDetails().getStatusHints();
    415   }
    416 
    417   public int getCameraDir() {
    418     return mCameraDirection;
    419   }
    420 
    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 
    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 
    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    */
    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 
    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 
    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
    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 
    595   public static void clearRestrictedCount() {
    596     sHiddenCounter = 0;
    597   }
    598 
    599   private boolean isHiddenNumber() {
    600     return getNumberPresentation() == TelecomManager.PRESENTATION_RESTRICTED
    601         || getNumberPresentation() == TelecomManager.PRESENTATION_UNKNOWN;
    602   }
    603 
    604   public boolean hasShownWiFiToLteHandoverToast() {
    605     return hasShownWiFiToLteHandoverToast;
    606   }
    607 
    608   public void setHasShownWiFiToLteHandoverToast() {
    609     hasShownWiFiToLteHandoverToast = true;
    610   }
    611 
    612   public boolean showWifiHandoverAlertAsToast() {
    613     return doNotShowDialogForHandoffToWifiFailure;
    614   }
    615 
    616   public void setDoNotShowDialogForHandoffToWifiFailure(boolean bool) {
    617     doNotShowDialogForHandoffToWifiFailure = bool;
    618   }
    619 
    620   public long getTimeAddedMs() {
    621     return mTimeAddedMs;
    622   }
    623 
    624   @Nullable
    625   public String getNumber() {
    626     return TelecomCallUtil.getNumber(mTelecomCall);
    627   }
    628 
    629   public void blockCall() {
    630     mTelecomCall.reject(false, null);
    631     setState(State.BLOCKED);
    632   }
    633 
    634   @Nullable
    635   public Uri getHandle() {
    636     return mTelecomCall == null ? null : mTelecomCall.getDetails().getHandle();
    637   }
    638 
    639   public boolean isEmergencyCall() {
    640     return mIsEmergencyCall;
    641   }
    642 
    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 
    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 
    670   public int getState() {
    671     if (mTelecomCall != null && mTelecomCall.getParent() != null) {
    672       return State.CONFERENCED;
    673     } else {
    674       return mState;
    675     }
    676   }
    677 
    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 
    688   public int getNumberPresentation() {
    689     return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getHandlePresentation();
    690   }
    691 
    692   public int getCnapNamePresentation() {
    693     return mTelecomCall == null ? -1 : mTelecomCall.getDetails().getCallerDisplayNamePresentation();
    694   }
    695 
    696   @Nullable
    697   public String getCnapName() {
    698     return mTelecomCall == null ? null : getTelecomCall().getDetails().getCallerDisplayName();
    699   }
    700 
    701   public Bundle getIntentExtras() {
    702     return mTelecomCall.getDetails().getIntentExtras();
    703   }
    704 
    705   @Nullable
    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. */
    711   public String getChildNumber() {
    712     return mChildNumber;
    713   }
    714 
    715   /** @return The last forwarded number for the call, or {@code null} if none specified. */
    716   public String getLastForwardedNumber() {
    717     return mLastForwardedNumber;
    718   }
    719 
    720   /** @return The call subject, or {@code null} if none specified. */
    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    */
    729   public boolean isCallSubjectSupported() {
    730     return mIsCallSubjectSupported;
    731   }
    732 
    733   /** Returns call disconnect cause, defined by {@link DisconnectCause}. */
    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 
    742   public void setDisconnectCause(DisconnectCause disconnectCause) {
    743     mDisconnectCause = disconnectCause;
    744     mLogState.disconnectCause = mDisconnectCause;
    745   }
    746 
    747   /** Returns the possible text message responses. */
    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. */
    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 
    769   public boolean hasProperty(int property) {
    770     return mTelecomCall.getDetails().hasProperty(property);
    771   }
    772 
    773   @NonNull
    774   public String getUniqueCallId() {
    775     return uniqueCallId;
    776   }
    777 
    778   /** Gets the time when the call first became active. */
    779   public long getConnectTimeMillis() {
    780     return mTelecomCall.getDetails().getConnectTimeMillis();
    781   }
    782 
    783   public boolean isConferenceCall() {
    784     return hasProperty(Call.Details.PROPERTY_CONFERENCE);
    785   }
    786 
    787   @Nullable
    788   public GatewayInfo getGatewayInfo() {
    789     return mTelecomCall == null ? null : mTelecomCall.getDetails().getGatewayInfo();
    790   }
    791 
    792   @Nullable
    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}. */
    798   public VideoCall getVideoCall() {
    799     return mTelecomCall == null ? null : mTelecomCall.getVideoCall();
    800   }
    801 
    802   public List<String> getChildCallIds() {
    803     return mChildCallIds;
    804   }
    805 
    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 
    814   public int getVideoState() {
    815     return mTelecomCall.getDetails().getVideoState();
    816   }
    817 
    818   public boolean isVideoCall() {
    819     return getVideoTech().isTransmittingOrReceiving();
    820   }
    821 
    822   public boolean hasReceivedVideoUpgradeRequest() {
    823     return VideoUtils.hasReceivedVideoUpgradeRequest(getVideoTech().getSessionModificationState());
    824   }
    825 
    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    */
    834   private void updateEmergencyCallState() {
    835     mIsEmergencyCall = TelecomCallUtil.isEmergencyCall(mTelecomCall);
    836   }
    837 
    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    */
    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    */
    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 
    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
    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 
    919   public String toSimpleString() {
    920     return super.toString();
    921   }
    922 
    923   @CallHistoryStatus
    924   public int getCallHistoryStatus() {
    925     return mCallHistoryStatus;
    926   }
    927 
    928   public void setCallHistoryStatus(@CallHistoryStatus int callHistoryStatus) {
    929     mCallHistoryStatus = callHistoryStatus;
    930   }
    931 
    932   public boolean didShowCameraPermission() {
    933     return didShowCameraPermission;
    934   }
    935 
    936   public void setDidShowCameraPermission(boolean didShow) {
    937     didShowCameraPermission = didShow;
    938   }
    939 
    940   public boolean isInGlobalSpamList() {
    941     return isInGlobalSpamList;
    942   }
    943 
    944   public void setIsInGlobalSpamList(boolean inSpamList) {
    945     isInGlobalSpamList = inSpamList;
    946   }
    947 
    948   public boolean isInUserSpamList() {
    949     return isInUserSpamList;
    950   }
    951 
    952   public void setIsInUserSpamList(boolean inSpamList) {
    953     isInUserSpamList = inSpamList;
    954   }
    955 
    956   public boolean isInUserWhiteList() {
    957     return isInUserWhiteList;
    958   }
    959 
    960   public void setIsInUserWhiteList(boolean inWhiteList) {
    961     isInUserWhiteList = inWhiteList;
    962   }
    963 
    964   public boolean isSpam() {
    965     return mIsSpam;
    966   }
    967 
    968   public void setSpam(boolean isSpam) {
    969     mIsSpam = isSpam;
    970   }
    971 
    972   public boolean isBlocked() {
    973     return mIsBlocked;
    974   }
    975 
    976   public void setBlockedStatus(boolean isBlocked) {
    977     mIsBlocked = isBlocked;
    978   }
    979 
    980   public boolean isRemotelyHeld() {
    981     return isRemotelyHeld;
    982   }
    983 
    984   public boolean isMergeInProcess() {
    985     return isMergeInProcess;
    986   }
    987 
    988   public boolean isIncoming() {
    989     return mLogState.isIncoming;
    990   }
    991 
    992   public LatencyReport getLatencyReport() {
    993     return mLatencyReport;
    994   }
    995 
    996   public int getAnswerAndReleaseButtonDisplayedTimes() {
    997     return answerAndReleaseButtonDisplayedTimes;
    998   }
    999 
   1000   public void increaseAnswerAndReleaseButtonDisplayedTimes() {
   1001     answerAndReleaseButtonDisplayedTimes++;
   1002   }
   1003 
   1004   public boolean getReleasedByAnsweringSecondCall() {
   1005     return releasedByAnsweringSecondCall;
   1006   }
   1007 
   1008   public void setReleasedByAnsweringSecondCall(boolean releasedByAnsweringSecondCall) {
   1009     this.releasedByAnsweringSecondCall = releasedByAnsweringSecondCall;
   1010   }
   1011 
   1012   public int getSecondCallWithoutAnswerAndReleasedButtonTimes() {
   1013     return secondCallWithoutAnswerAndReleasedButtonTimes;
   1014   }
   1015 
   1016   public void increaseSecondCallWithoutAnswerAndReleasedButtonTimes() {
   1017     secondCallWithoutAnswerAndReleasedButtonTimes++;
   1018   }
   1019 
   1020   @Nullable
   1021   public EnrichedCallCapabilities getEnrichedCallCapabilities() {
   1022     return mEnrichedCallCapabilities;
   1023   }
   1024 
   1025   public void setEnrichedCallCapabilities(
   1026       @Nullable EnrichedCallCapabilities mEnrichedCallCapabilities) {
   1027     this.mEnrichedCallCapabilities = mEnrichedCallCapabilities;
   1028   }
   1029 
   1030   @Nullable
   1031   public Session getEnrichedCallSession() {
   1032     return mEnrichedCallSession;
   1033   }
   1034 
   1035   public void setEnrichedCallSession(@Nullable Session mEnrichedCallSession) {
   1036     this.mEnrichedCallSession = mEnrichedCallSession;
   1037   }
   1038 
   1039   public void unregisterCallback() {
   1040     mTelecomCall.unregisterCallback(mTelecomCallCallback);
   1041   }
   1042 
   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 
   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 
   1061   public void hold() {
   1062     LogUtil.i("DialerCall.hold", "");
   1063     mTelecomCall.hold();
   1064   }
   1065 
   1066   public void unhold() {
   1067     LogUtil.i("DialerCall.unhold", "");
   1068     mTelecomCall.unhold();
   1069   }
   1070 
   1071   public void splitFromConference() {
   1072     LogUtil.i("DialerCall.splitFromConference", "");
   1073     mTelecomCall.splitFromConference();
   1074   }
   1075 
   1076   public void answer(int videoState) {
   1077     LogUtil.i("DialerCall.answer", "videoState: " + videoState);
   1078     mTelecomCall.answer(videoState);
   1079   }
   1080 
   1081   public void answer() {
   1082     answer(mTelecomCall.getDetails().getVideoState());
   1083   }
   1084 
   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 */
   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 
   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 
   1116   public VideoTech getVideoTech() {
   1117     return mVideoTechManager.getVideoTech();
   1118   }
   1119 
   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 
   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
   1172   public void onVideoTechStateChanged() {
   1173     update();
   1174   }
   1175 
   1176   @Override
   1177   public void onSessionModificationStateChanged() {
   1178     for (DialerCallListener listener : mListeners) {
   1179       listener.onDialerCallSessionModificationStateChange();
   1180     }
   1181   }
   1182 
   1183   @Override
   1184   public void onCameraDimensionsChanged(int width, int height) {
   1185     InCallVideoCallCallbackNotifier.getInstance().cameraDimensionsChanged(this, width, height);
   1186   }
   1187 
   1188   @Override
   1189   public void onPeerDimensionsChanged(int width, int height) {
   1190     InCallVideoCallCallbackNotifier.getInstance().peerDimensionsChanged(this, width, height);
   1191   }
   1192 
   1193   @Override
   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
   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
   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
   1246   public void onEnrichedCallStateChanged() {
   1247     updateEnrichedCallSession();
   1248   }
   1249 
   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 
   1286   private void dispatchOnEnrichedCallSessionUpdate() {
   1287     for (DialerCallListener listener : mListeners) {
   1288       listener.onEnrichedCallSessionUpdate();
   1289     }
   1290   }
   1291 
   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 
   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 
   1346     public static boolean isDialing(int state) {
   1347       return state == DIALING || state == PULLING || state == REDIALING;
   1348     }
   1349 
   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 
   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 
   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
   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 
   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 
   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 
   1538     void dispatchCallStateChanged(int newState) {
   1539       for (VideoTech videoTech : videoTechs) {
   1540         videoTech.onCallStateChanged(context, newState);
   1541       }
   1542     }
   1543 
   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 {
   1553     void onCannedTextResponsesLoaded(DialerCall call);
   1554   }
   1555 }
   1556