• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.internal.telephony.imsphone;
18 
19 import static android.telephony.CarrierConfigManager.USSD_OVER_CS_PREFERRED;
20 import static android.telephony.CarrierConfigManager.USSD_OVER_IMS_ONLY;
21 
22 import static com.android.internal.annotations.VisibleForTesting.Visibility.PRIVATE;
23 import static com.android.internal.telephony.Phone.CS_FALLBACK;
24 
25 import android.annotation.NonNull;
26 import android.annotation.Nullable;
27 import android.app.usage.NetworkStatsManager;
28 import android.compat.annotation.UnsupportedAppUsage;
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.content.SharedPreferences;
34 import android.content.pm.PackageManager;
35 import android.net.ConnectivityManager;
36 import android.net.Network;
37 import android.net.NetworkCapabilities;
38 import android.net.NetworkInfo;
39 import android.net.NetworkRequest;
40 import android.net.NetworkStats;
41 import android.net.netstats.provider.NetworkStatsProvider;
42 import android.os.AsyncResult;
43 import android.os.Build;
44 import android.os.Bundle;
45 import android.os.Handler;
46 import android.os.Message;
47 import android.os.ParcelUuid;
48 import android.os.PersistableBundle;
49 import android.os.Registrant;
50 import android.os.RegistrantList;
51 import android.os.RemoteException;
52 import android.os.SystemClock;
53 import android.preference.PreferenceManager;
54 import android.provider.Settings;
55 import android.sysprop.TelephonyProperties;
56 import android.telecom.Connection.VideoProvider;
57 import android.telecom.TelecomManager;
58 import android.telecom.VideoProfile;
59 import android.telephony.CallQuality;
60 import android.telephony.CarrierConfigManager;
61 import android.telephony.DisconnectCause;
62 import android.telephony.PhoneNumberUtils;
63 import android.telephony.ServiceState;
64 import android.telephony.SubscriptionInfo;
65 import android.telephony.SubscriptionManager;
66 import android.telephony.TelephonyLocalConnection;
67 import android.telephony.TelephonyManager;
68 import android.telephony.emergency.EmergencyNumber;
69 import android.telephony.ims.ImsCallProfile;
70 import android.telephony.ims.ImsCallSession;
71 import android.telephony.ims.ImsConferenceState;
72 import android.telephony.ims.ImsMmTelManager;
73 import android.telephony.ims.ImsReasonInfo;
74 import android.telephony.ims.ImsStreamMediaProfile;
75 import android.telephony.ims.ImsSuppServiceNotification;
76 import android.telephony.ims.ProvisioningManager;
77 import android.telephony.ims.RtpHeaderExtension;
78 import android.telephony.ims.RtpHeaderExtensionType;
79 import android.telephony.ims.feature.ImsFeature;
80 import android.telephony.ims.feature.MmTelFeature;
81 import android.telephony.ims.stub.ImsRegistrationImplBase;
82 import android.text.TextUtils;
83 import android.util.ArrayMap;
84 import android.util.ArraySet;
85 import android.util.LocalLog;
86 import android.util.Log;
87 import android.util.Pair;
88 import android.util.SparseIntArray;
89 
90 import com.android.ims.FeatureConnector;
91 import com.android.ims.ImsCall;
92 import com.android.ims.ImsConfig;
93 import com.android.ims.ImsEcbm;
94 import com.android.ims.ImsException;
95 import com.android.ims.ImsManager;
96 import com.android.ims.ImsUtInterface;
97 import com.android.ims.internal.ConferenceParticipant;
98 import com.android.ims.internal.IImsCallSession;
99 import com.android.ims.internal.IImsVideoCallProvider;
100 import com.android.ims.internal.ImsVideoCallProviderWrapper;
101 import com.android.ims.internal.VideoPauseTracker;
102 import com.android.internal.annotations.VisibleForTesting;
103 import com.android.internal.os.SomeArgs;
104 import com.android.internal.telephony.Call;
105 import com.android.internal.telephony.CallFailCause;
106 import com.android.internal.telephony.CallStateException;
107 import com.android.internal.telephony.CallTracker;
108 import com.android.internal.telephony.CommandException;
109 import com.android.internal.telephony.CommandsInterface;
110 import com.android.internal.telephony.Connection;
111 import com.android.internal.telephony.IccCardConstants;
112 import com.android.internal.telephony.LocaleTracker;
113 import com.android.internal.telephony.Phone;
114 import com.android.internal.telephony.PhoneConstants;
115 import com.android.internal.telephony.ServiceStateTracker;
116 import com.android.internal.telephony.SubscriptionController;
117 import com.android.internal.telephony.d2d.RtpTransport;
118 import com.android.internal.telephony.data.DataSettingsManager;
119 import com.android.internal.telephony.dataconnection.DataEnabledSettings;
120 import com.android.internal.telephony.dataconnection.DataEnabledSettings.DataEnabledChangedReason;
121 import com.android.internal.telephony.emergency.EmergencyNumberTracker;
122 import com.android.internal.telephony.gsm.SuppServiceNotification;
123 import com.android.internal.telephony.imsphone.ImsPhone.ImsDialArgs;
124 import com.android.internal.telephony.metrics.CallQualityMetrics;
125 import com.android.internal.telephony.metrics.TelephonyMetrics;
126 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession;
127 import com.android.internal.telephony.nano.TelephonyProto.TelephonyCallSession.Event.ImsCommand;
128 import com.android.internal.telephony.util.TelephonyUtils;
129 import com.android.internal.util.IndentingPrintWriter;
130 import com.android.telephony.Rlog;
131 
132 import java.io.FileDescriptor;
133 import java.io.PrintWriter;
134 import java.util.ArrayList;
135 import java.util.HashMap;
136 import java.util.List;
137 import java.util.Map;
138 import java.util.Objects;
139 import java.util.Optional;
140 import java.util.Queue;
141 import java.util.Set;
142 import java.util.concurrent.CancellationException;
143 import java.util.concurrent.CompletableFuture;
144 import java.util.concurrent.CompletionException;
145 import java.util.concurrent.ConcurrentHashMap;
146 import java.util.concurrent.ConcurrentLinkedQueue;
147 import java.util.concurrent.Executor;
148 import java.util.concurrent.LinkedBlockingQueue;
149 import java.util.concurrent.atomic.AtomicInteger;
150 import java.util.function.Consumer;
151 import java.util.regex.Pattern;
152 
153 /**
154  * {@hide}
155  */
156 public class ImsPhoneCallTracker extends CallTracker implements ImsPullCall {
157     static final String LOG_TAG = "ImsPhoneCallTracker";
158     static final String VERBOSE_STATE_TAG = "IPCTState";
159 
160     /**
161      * Class which contains configuration items obtained from the config.xml in
162      * packages/services/Telephony which are injected in the ImsPhoneCallTracker at phone creation
163      * time.
164      */
165     public static class Config {
166         /**
167          * The value for config.xml/config_use_device_to_device_communication.
168          * When {@code true}, the device supports device to device communication using both DTMF
169          * and RTP header extensions.
170          */
171         public boolean isD2DCommunicationSupported;
172     }
173 
174     public interface PhoneStateListener {
onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState)175         void onPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState);
176     }
177 
178     public interface SharedPreferenceProxy {
getDefaultSharedPreferences(Context context)179         SharedPreferences getDefaultSharedPreferences(Context context);
180     }
181 
182     private static final boolean DBG = true;
183 
184     // When true, dumps the state of ImsPhoneCallTracker after changes to foreground and background
185     // calls.  This is helpful for debugging.  It is also possible to enable this at runtime by
186     // setting the IPCTState log tag to VERBOSE.
187     private static final boolean FORCE_VERBOSE_STATE_LOGGING = false; /* stopship if true */
188     private static final boolean VERBOSE_STATE_LOGGING = FORCE_VERBOSE_STATE_LOGGING ||
189             Rlog.isLoggable(VERBOSE_STATE_TAG, Log.VERBOSE);
190     private static final int CONNECTOR_RETRY_DELAY_MS = 5000; // 5 seconds.
191 
192     private MmTelFeature.MmTelCapabilities mMmTelCapabilities =
193             new MmTelFeature.MmTelCapabilities();
194 
195     private TelephonyMetrics mMetrics;
196     private final Map<String, CallQualityMetrics> mCallQualityMetrics = new ConcurrentHashMap<>();
197     private final ConcurrentLinkedQueue<CallQualityMetrics> mCallQualityMetricsHistory =
198             new ConcurrentLinkedQueue<>();
199     // True if there is a carrier config loaded for a specific subscription (and not the default
200     // configuration).
201     private boolean mCarrierConfigLoadedForSubscription = false;
202     // Cache the latest carrier config received for a subscription. The configuration will be
203     // applied to the ImsService when startListeningForCalls is called.
204     private Pair<Integer, PersistableBundle> mCarrierConfigForSubId = null;
205     // The subId of the last ImsService attached to this tracker or empty if there has not been
206     // an attached ImsService yet.
207     private Optional<Integer> mCurrentlyConnectedSubId = Optional.empty();
208 
209     private final MmTelFeatureListener mMmTelFeatureListener = new MmTelFeatureListener();
210     private class MmTelFeatureListener extends MmTelFeature.Listener {
211 
processIncomingCall(IImsCallSession c, Bundle extras)212         private void processIncomingCall(IImsCallSession c, Bundle extras) {
213             if (DBG) log("processIncomingCall: incoming call intent");
214 
215             if (extras == null) extras = new Bundle();
216             if (mImsManager == null) return;
217 
218             try {
219                 // Network initiated USSD will be treated by mImsUssdListener
220                 boolean isUssd = extras.getBoolean(MmTelFeature.EXTRA_IS_USSD, false);
221                 // For compatibility purposes with older vendor implmentations.
222                 isUssd |= extras.getBoolean(ImsManager.EXTRA_USSD, false);
223                 if (isUssd) {
224                     if (DBG) log("processIncomingCall: USSD");
225                     mOperationLocalLog.log("processIncomingCall: USSD");
226                     mUssdSession = mImsManager.takeCall(c, mImsUssdListener);
227                     if (mUssdSession != null) {
228                         mUssdSession.accept(ImsCallProfile.CALL_TYPE_VOICE);
229                     }
230                     return;
231                 }
232 
233                 boolean isUnknown = extras.getBoolean(MmTelFeature.EXTRA_IS_UNKNOWN_CALL, false);
234                 // For compatibility purposes with older vendor implmentations.
235                 isUnknown |= extras.getBoolean(ImsManager.EXTRA_IS_UNKNOWN_CALL, false);
236                 if (DBG) {
237                     log("processIncomingCall: isUnknown = " + isUnknown
238                             + " fg = " + mForegroundCall.getState()
239                             + " bg = " + mBackgroundCall.getState());
240                 }
241 
242                 // Normal MT/Unknown call
243                 ImsCall imsCall = mImsManager.takeCall(c, mImsCallListener);
244                 ImsPhoneConnection conn = new ImsPhoneConnection(mPhone, imsCall,
245                         ImsPhoneCallTracker.this,
246                         (isUnknown ? mForegroundCall : mRingingCall), isUnknown);
247 
248                 // If there is an active call.
249                 if (mForegroundCall.hasConnections()) {
250                     ImsCall activeCall = mForegroundCall.getFirstConnection().getImsCall();
251                     if (activeCall != null && imsCall != null) {
252                         // activeCall could be null if the foreground call is in a disconnected
253                         // state.  If either of the calls is null there is no need to check if
254                         // one will be disconnected on answer.
255                         boolean answeringWillDisconnect =
256                                 shouldDisconnectActiveCallOnAnswer(activeCall, imsCall);
257                         conn.setActiveCallDisconnectedOnAnswer(answeringWillDisconnect);
258                     }
259                 }
260                 conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
261                 conn.setAllowHoldingVideoCall(mAllowHoldingVideoCall);
262 
263                 if ((c != null) && (c.getCallProfile() != null)
264                         && (c.getCallProfile().getCallExtras() != null)
265                         && (c.getCallProfile().getCallExtras()
266                           .containsKey(ImsCallProfile.EXTRA_CALL_DISCONNECT_CAUSE))) {
267                     String error = c.getCallProfile()
268                             .getCallExtra(ImsCallProfile.EXTRA_CALL_DISCONNECT_CAUSE, null);
269                     if (error != null) {
270                         try {
271                             int cause = getDisconnectCauseFromReasonInfo(
272                                         new ImsReasonInfo(Integer.parseInt(error), 0, null),
273                                     conn.getState());
274                             if (cause == DisconnectCause.INCOMING_AUTO_REJECTED) {
275                                 conn.setDisconnectCause(cause);
276                                 if (DBG) log("onIncomingCall : incoming call auto rejected");
277                                 mOperationLocalLog.log("processIncomingCall: auto rejected");
278                             }
279                         } catch (NumberFormatException e) {
280                             Rlog.e(LOG_TAG, "Exception in parsing Integer Data: " + e);
281                         }
282                     }
283                 }
284 
285                 mOperationLocalLog.log("onIncomingCall: isUnknown=" + isUnknown + ", connId="
286                         + System.identityHashCode(conn));
287 
288                 addConnection(conn);
289 
290                 setVideoCallProvider(conn, imsCall);
291 
292                 TelephonyMetrics.getInstance().writeOnImsCallReceive(mPhone.getPhoneId(),
293                         imsCall.getSession());
294                 mPhone.getVoiceCallSessionStats().onImsCallReceived(conn);
295 
296                 if (isUnknown) {
297                     // Check for condition where an unknown connection replaces a pending
298                     // MO call.  This will cause problems later in all likelihood.
299                     if (mPendingMO != null
300                             && Objects.equals(mPendingMO.getAddress(), conn.getAddress())) {
301                         mOperationLocalLog.log("onIncomingCall: unknown call " + conn
302                                 + " replaces " + mPendingMO);
303                     }
304                     mPhone.notifyUnknownConnection(conn);
305                 } else {
306                     if ((mForegroundCall.getState() != ImsPhoneCall.State.IDLE)
307                             || (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE)) {
308                         conn.update(imsCall, ImsPhoneCall.State.WAITING);
309                     }
310 
311                     mPhone.notifyNewRingingConnection(conn);
312                     mPhone.notifyIncomingRing();
313                 }
314 
315                 updatePhoneState();
316                 mPhone.notifyPreciseCallStateChanged();
317             } catch (ImsException | RemoteException e) {
318                 loge("processIncomingCall: exception " + e);
319                 mOperationLocalLog.log("onIncomingCall: exception processing: "  + e);
320             }
321         }
322 
323         @Override
onIncomingCall(IImsCallSession c, Bundle extras)324         public void onIncomingCall(IImsCallSession c, Bundle extras) {
325             // we want to ensure we block this binder thread until incoming call setup completes
326             // as to avoid race conditions where the ImsService tries to update the state of the
327             // call before the listeners have been attached.
328             executeAndWait(()-> processIncomingCall(c, extras));
329         }
330 
331         @Override
onVoiceMessageCountUpdate(int count)332         public void onVoiceMessageCountUpdate(int count) {
333             TelephonyUtils.runWithCleanCallingIdentity(()-> {
334                 if (mPhone != null && mPhone.mDefaultPhone != null) {
335                     if (DBG) log("onVoiceMessageCountChanged :: count=" + count);
336                     mPhone.mDefaultPhone.setVoiceMessageCount(count);
337                 } else {
338                     loge("onVoiceMessageCountUpdate: null phone");
339                 }
340             }, mExecutor);
341         }
342 
343         /**
344          * Schedule the given Runnable on mExecutor and block this thread until it finishes.
345          * @param r The Runnable to run.
346          */
executeAndWait(Runnable r)347         private void executeAndWait(Runnable r) {
348             try {
349                 CompletableFuture.runAsync(
350                         () -> TelephonyUtils.runWithCleanCallingIdentity(r), mExecutor).join();
351             } catch (CancellationException | CompletionException e) {
352                 logw("Binder - exception: " + e.getMessage());
353             }
354         }
355     }
356 
357     /**
358      * A class implementing {@link NetworkStatsProvider} to report VT data usage to system.
359      */
360     // TODO: Directly reports diff in updateVtDataUsage.
361     @VisibleForTesting(visibility = PRIVATE)
362     public class VtDataUsageProvider extends NetworkStatsProvider {
363         private int mToken = 0;
364         private NetworkStats mIfaceSnapshot = new NetworkStats(0L, 0);
365         private NetworkStats mUidSnapshot = new NetworkStats(0L, 0);
366         @Override
onRequestStatsUpdate(int token)367         public void onRequestStatsUpdate(int token) {
368             // If there is an ongoing VT call, request the latest VT usage from the modem. The
369             // latest usage will return asynchronously so it won't be counted in this round, but it
370             // will be eventually counted when next requestStatsUpdate is called.
371             if (mState != PhoneConstants.State.IDLE) {
372                 for (ImsPhoneConnection conn : mConnections) {
373                     final VideoProvider videoProvider = conn.getVideoProvider();
374                     if (videoProvider != null) {
375                         videoProvider.onRequestConnectionDataUsage();
376                     }
377                 }
378             }
379 
380             final NetworkStats ifaceDiff = mVtDataUsageSnapshot.subtract(mIfaceSnapshot);
381             final NetworkStats uidDiff = mVtDataUsageUidSnapshot.subtract(mUidSnapshot);
382             mVtDataUsageProvider.notifyStatsUpdated(mToken, ifaceDiff, uidDiff);
383             mIfaceSnapshot = mIfaceSnapshot.add(ifaceDiff);
384             mUidSnapshot = mUidSnapshot.add(uidDiff);
385             mToken = token;
386         }
387 
388         @Override
onSetLimit(String iface, long quotaBytes)389         public void onSetLimit(String iface, long quotaBytes) {
390             // No-op
391         }
392 
393         @Override
onSetAlert(long quotaBytes)394         public void onSetAlert(long quotaBytes) {
395             // No-op
396         }
397     }
398 
399     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
400         @Override
401         public void onReceive(Context context, Intent intent) {
402             if (intent.getAction().equals(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED)) {
403                 int subId = intent.getIntExtra(CarrierConfigManager.EXTRA_SUBSCRIPTION_INDEX,
404                         SubscriptionManager.INVALID_SUBSCRIPTION_ID);
405                 int phoneId = intent.getIntExtra(CarrierConfigManager.EXTRA_SLOT_INDEX,
406                         SubscriptionManager.INVALID_PHONE_INDEX);
407                 if (mPhone.getPhoneId() != phoneId) {
408                     log("onReceive: Skipping indication for other phoneId: " + phoneId);
409                     return;
410                 }
411                 PersistableBundle carrierConfig = getCarrierConfigBundle(subId);
412                 mCarrierConfigForSubId = new Pair<>(subId, carrierConfig);
413                 if (!mCurrentlyConnectedSubId.isEmpty()
414                         && subId == mCurrentlyConnectedSubId.get()) {
415                     log("onReceive: Applying carrier config for subId: " + subId);
416                     updateCarrierConfiguration(subId, carrierConfig);
417                 } else {
418                     // cache the latest config update until ImsService connects for this subId.
419                     // Once it has connected, startListeningForCalls will apply the config.
420                     log("onReceive: caching carrier config until ImsService connects for subId: "
421                             + subId);
422                 }
423             } else if (TelecomManager.ACTION_DEFAULT_DIALER_CHANGED.equals(intent.getAction())) {
424                 mDefaultDialerUid.set(getPackageUid(context, intent.getStringExtra(
425                         TelecomManager.EXTRA_CHANGE_DEFAULT_DIALER_PACKAGE_NAME)));
426             }
427         }
428     };
429 
430     /**
431      * Tracks whether we are currently monitoring network connectivity for the purpose of warning
432      * the user of an inability to handover from LTE to WIFI for video calls.
433      */
434     private boolean mIsMonitoringConnectivity = false;
435 
436     /**
437      * A test flag which can be used to disable processing of the conference event package data
438      * received from the network.
439      */
440     private boolean mIsConferenceEventPackageEnabled = true;
441 
442     /**
443      * The Telephony config.xml values pertinent to ImsPhoneCallTracker.
444      */
445     private Config mConfig = null;
446 
447     /**
448      * Whether D2D has been force enabled via the d2d telephony command.
449      */
450     private boolean mDeviceToDeviceForceEnabled = false;
451 
452     /**
453      * Network callback used to schedule the handover check when a wireless network connects.
454      */
455     private ConnectivityManager.NetworkCallback mNetworkCallback =
456             new ConnectivityManager.NetworkCallback() {
457                 @Override
458                 public void onAvailable(Network network) {
459                     Rlog.i(LOG_TAG, "Network available: " + network);
460                     scheduleHandoverCheck();
461                 }
462             };
463 
464     //***** Constants
465 
466     static final int MAX_CONNECTIONS = 7;
467     static final int MAX_CONNECTIONS_PER_CALL = 5;
468 
469     // Max number of calls we will keep call quality history for (the history is saved in-memory and
470     // included in bug reports).
471     private static final int MAX_CALL_QUALITY_HISTORY = 10;
472 
473     private static final int EVENT_HANGUP_PENDINGMO = 18;
474     private static final int EVENT_DIAL_PENDINGMO = 20;
475     private static final int EVENT_EXIT_ECBM_BEFORE_PENDINGMO = 21;
476     private static final int EVENT_VT_DATA_USAGE_UPDATE = 22;
477     private static final int EVENT_DATA_ENABLED_CHANGED = 23;
478     private static final int EVENT_CHECK_FOR_WIFI_HANDOVER = 25;
479     private static final int EVENT_ON_FEATURE_CAPABILITY_CHANGED = 26;
480     private static final int EVENT_SUPP_SERVICE_INDICATION = 27;
481     private static final int EVENT_REDIAL_WIFI_E911_CALL = 28;
482     private static final int EVENT_REDIAL_WIFI_E911_TIMEOUT = 29;
483     private static final int EVENT_ANSWER_WAITING_CALL = 30;
484     private static final int EVENT_RESUME_NOW_FOREGROUND_CALL = 31;
485     private static final int EVENT_REDIAL_WITHOUT_RTT = 32;
486 
487     private static final int TIMEOUT_HANGUP_PENDINGMO = 500;
488 
489     private static final int HANDOVER_TO_WIFI_TIMEOUT_MS = 60000; // ms
490 
491     private static final int TIMEOUT_REDIAL_WIFI_E911_MS = 10000;
492 
493     private static final int TIMEOUT_PARTICIPANT_CONNECT_TIME_CACHE_MS = 60000; //ms
494 
495     // Following values are for mHoldSwitchingState
496     private enum HoldSwapState {
497         // Not in the middle of a hold/swap operation
498         INACTIVE,
499         // Pending a single call getting held
500         PENDING_SINGLE_CALL_HOLD,
501         // Pending a single call getting unheld
502         PENDING_SINGLE_CALL_UNHOLD,
503         // Pending swapping a active and a held call
504         SWAPPING_ACTIVE_AND_HELD,
505         // Pending holding a call to answer a call-waiting call
506         HOLDING_TO_ANSWER_INCOMING,
507         // Pending resuming the foreground call after some kind of failure
508         PENDING_RESUME_FOREGROUND_AFTER_FAILURE,
509         // Pending holding a call to dial another outgoing call
510         HOLDING_TO_DIAL_OUTGOING,
511         // Pending resuming the foreground call after it has completed an ongoing hold operation.
512         PENDING_RESUME_FOREGROUND_AFTER_HOLD
513     }
514 
515     //***** Instance Variables
516     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
517     private ArrayList<ImsPhoneConnection> mConnections = new ArrayList<ImsPhoneConnection>();
518     private RegistrantList mVoiceCallEndedRegistrants = new RegistrantList();
519     private RegistrantList mVoiceCallStartedRegistrants = new RegistrantList();
520 
521     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
522     public ImsPhoneCall mRingingCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_RINGING);
523     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
524     public ImsPhoneCall mForegroundCall = new ImsPhoneCall(this,
525             ImsPhoneCall.CONTEXT_FOREGROUND);
526     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
527     public ImsPhoneCall mBackgroundCall = new ImsPhoneCall(this,
528             ImsPhoneCall.CONTEXT_BACKGROUND);
529     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
530     public ImsPhoneCall mHandoverCall = new ImsPhoneCall(this, ImsPhoneCall.CONTEXT_HANDOVER);
531 
532     // Hold aggregated video call data usage for each video call since boot.
533     // The ImsCall's call id is the key of the map.
534     private final HashMap<Integer, Long> mVtDataUsageMap = new HashMap<>();
535     private final Map<String, CacheEntry> mPhoneNumAndConnTime = new ConcurrentHashMap<>();
536     private final Queue<CacheEntry> mUnknownPeerConnTime = new LinkedBlockingQueue<>();
537 
538     private static class CacheEntry {
539         private long mCachedTime;
540         private long mConnectTime;
541         private long mConnectElapsedTime;
542         /**
543          * The direction of the call;
544          * {@link android.telecom.Call.Details#DIRECTION_INCOMING} for incoming calls, or
545          * {@link android.telecom.Call.Details#DIRECTION_OUTGOING} for outgoing calls.
546          */
547         private int mCallDirection;
548 
CacheEntry(long cachedTime, long connectTime, long connectElapsedTime, int callDirection)549         CacheEntry(long cachedTime, long connectTime, long connectElapsedTime, int callDirection) {
550             mCachedTime = cachedTime;
551             mConnectTime = connectTime;
552             mConnectElapsedTime = connectElapsedTime;
553             mCallDirection = callDirection;
554         }
555     }
556 
557     private volatile NetworkStats mVtDataUsageSnapshot = null;
558     private volatile NetworkStats mVtDataUsageUidSnapshot = null;
559     private final VtDataUsageProvider mVtDataUsageProvider = new VtDataUsageProvider();
560 
561     private final AtomicInteger mDefaultDialerUid = new AtomicInteger(NetworkStats.UID_ALL);
562 
563     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
564     private ImsPhoneConnection mPendingMO;
565     private int mClirMode = CommandsInterface.CLIR_DEFAULT;
566     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
567     private Object mSyncHold = new Object();
568 
569     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
570     private ImsCall mUssdSession = null;
571     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
572     private Message mPendingUssd = null;
573 
574     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
575     ImsPhone mPhone;
576 
577     private boolean mDesiredMute = false;    // false = mute off
578     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
579     private boolean mOnHoldToneStarted = false;
580     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
581     private int mOnHoldToneId = -1;
582 
583     private PhoneConstants.State mState = PhoneConstants.State.IDLE;
584 
585     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
586     private ImsManager mImsManager;
587     private ImsUtInterface mUtInterface;
588 
589     private Call.SrvccState mSrvccState = Call.SrvccState.NONE;
590 
591     private boolean mIsInEmergencyCall = false;
592     private boolean mIsDataEnabled = false;
593 
594     private int pendingCallClirMode;
595     private int mPendingCallVideoState;
596     private Bundle mPendingIntentExtras;
597     private boolean pendingCallInEcm = false;
598     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
599     private boolean mSwitchingFgAndBgCalls = false;
600     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
601     private ImsCall mCallExpectedToResume = null;
602     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
603     private boolean mAllowEmergencyVideoCalls = false;
604     private boolean mIgnoreDataEnabledChangedForVideoCalls = false;
605     private boolean mIsViLteDataMetered = false;
606     private boolean mAlwaysPlayRemoteHoldTone = false;
607     private boolean mAutoRetryFailedWifiEmergencyCall = false;
608     private boolean mSupportCepOnPeer = true;
609     private boolean mSupportD2DUsingRtp = false;
610     private boolean mSupportSdpForRtpHeaderExtensions = false;
611     // Tracks the state of our background/foreground calls while a call hold/swap operation is
612     // in progress. Values listed above.
613     private HoldSwapState mHoldSwitchingState = HoldSwapState.INACTIVE;
614 
615     private String mLastDialString = null;
616     private ImsDialArgs mLastDialArgs = null;
617     private Executor mExecutor = Runnable::run;
618 
619     /**
620      * Listeners to changes in the phone state.  Intended for use by other interested IMS components
621      * without the need to register a full blown {@link android.telephony.PhoneStateListener}.
622      */
623     private List<PhoneStateListener> mPhoneStateListeners = new ArrayList<>();
624 
625     /**
626      * Carrier configuration option which determines if video calls which have been downgraded to an
627      * audio call should be treated as if they are still video calls.
628      */
629     private boolean mTreatDowngradedVideoCallsAsVideoCalls = false;
630 
631     /**
632      * Carrier configuration option which determines if an ongoing video call over wifi should be
633      * dropped when an audio call is answered.
634      */
635     private boolean mDropVideoCallWhenAnsweringAudioCall = false;
636 
637     /**
638      * Carrier configuration option which determines whether adding a call during a video call
639      * should be allowed.
640      */
641     private boolean mAllowAddCallDuringVideoCall = true;
642 
643     /**
644      * Carrier configuration option which determines whether holding a video call
645      * should be allowed.
646      */
647     private boolean mAllowHoldingVideoCall = true;
648 
649     /**
650      * Carrier configuration option which determines whether to notify the connection if a handover
651      * to wifi fails.
652      */
653     private boolean mNotifyVtHandoverToWifiFail = false;
654 
655     /**
656      * Carrier configuration option which determines whether the carrier supports downgrading a
657      * TX/RX/TX-RX video call directly to an audio-only call.
658      */
659     private boolean mSupportDowngradeVtToAudio = false;
660 
661     /**
662      * Stores the mapping of {@code ImsReasonInfo#CODE_*} to {@code CallFailCause#*}
663      */
664     private static final SparseIntArray PRECISE_CAUSE_MAP = new SparseIntArray();
665     static {
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT, CallFailCause.LOCAL_ILLEGAL_ARGUMENT)666         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_ARGUMENT,
667                 CallFailCause.LOCAL_ILLEGAL_ARGUMENT);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE, CallFailCause.LOCAL_ILLEGAL_STATE)668         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE,
669                 CallFailCause.LOCAL_ILLEGAL_STATE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR, CallFailCause.LOCAL_INTERNAL_ERROR)670         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_INTERNAL_ERROR,
671                 CallFailCause.LOCAL_INTERNAL_ERROR);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN, CallFailCause.LOCAL_IMS_SERVICE_DOWN)672         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN,
673                 CallFailCause.LOCAL_IMS_SERVICE_DOWN);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL, CallFailCause.LOCAL_NO_PENDING_CALL)674         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NO_PENDING_CALL,
675                 CallFailCause.LOCAL_NO_PENDING_CALL);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE, CallFailCause.NORMAL_CLEARING)676         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE,
677                 CallFailCause.NORMAL_CLEARING);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_POWER_OFF, CallFailCause.LOCAL_POWER_OFF)678         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_POWER_OFF,
679                 CallFailCause.LOCAL_POWER_OFF);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY, CallFailCause.LOCAL_LOW_BATTERY)680         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_LOW_BATTERY,
681                 CallFailCause.LOCAL_LOW_BATTERY);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE, CallFailCause.LOCAL_NETWORK_NO_SERVICE)682         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE,
683                 CallFailCause.LOCAL_NETWORK_NO_SERVICE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE, CallFailCause.LOCAL_NETWORK_NO_LTE_COVERAGE)684         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE,
685                 CallFailCause.LOCAL_NETWORK_NO_LTE_COVERAGE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING, CallFailCause.LOCAL_NETWORK_ROAMING)686         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING,
687                 CallFailCause.LOCAL_NETWORK_ROAMING);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED, CallFailCause.LOCAL_NETWORK_IP_CHANGED)688         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED,
689                 CallFailCause.LOCAL_NETWORK_IP_CHANGED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE, CallFailCause.LOCAL_SERVICE_UNAVAILABLE)690         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE,
691                 CallFailCause.LOCAL_SERVICE_UNAVAILABLE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED, CallFailCause.LOCAL_NOT_REGISTERED)692         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED,
693                 CallFailCause.LOCAL_NOT_REGISTERED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_EXCEEDED, CallFailCause.LOCAL_MAX_CALL_EXCEEDED)694         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_EXCEEDED,
695                 CallFailCause.LOCAL_MAX_CALL_EXCEEDED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_DECLINE, CallFailCause.LOCAL_CALL_DECLINE)696         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_DECLINE,
697                 CallFailCause.LOCAL_CALL_DECLINE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING, CallFailCause.LOCAL_CALL_VCC_ON_PROGRESSING)698         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING,
699                 CallFailCause.LOCAL_CALL_VCC_ON_PROGRESSING);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED, CallFailCause.LOCAL_CALL_RESOURCE_RESERVATION_FAILED)700         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_RESOURCE_RESERVATION_FAILED,
701                 CallFailCause.LOCAL_CALL_RESOURCE_RESERVATION_FAILED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED, CallFailCause.LOCAL_CALL_CS_RETRY_REQUIRED)702         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED,
703                 CallFailCause.LOCAL_CALL_CS_RETRY_REQUIRED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED, CallFailCause.LOCAL_CALL_VOLTE_RETRY_REQUIRED)704         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_VOLTE_RETRY_REQUIRED,
705                 CallFailCause.LOCAL_CALL_VOLTE_RETRY_REQUIRED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED, CallFailCause.LOCAL_CALL_TERMINATED)706         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED,
707                 CallFailCause.LOCAL_CALL_TERMINATED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE, CallFailCause.LOCAL_HO_NOT_FEASIBLE)708         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOCAL_HO_NOT_FEASIBLE,
709                 CallFailCause.LOCAL_HO_NOT_FEASIBLE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING, CallFailCause.TIMEOUT_1XX_WAITING)710         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING,
711                 CallFailCause.TIMEOUT_1XX_WAITING);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER, CallFailCause.TIMEOUT_NO_ANSWER)712         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER,
713                 CallFailCause.TIMEOUT_NO_ANSWER);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE, CallFailCause.TIMEOUT_NO_ANSWER_CALL_UPDATE)714         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE,
715                 CallFailCause.TIMEOUT_NO_ANSWER_CALL_UPDATE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_FDN_BLOCKED, CallFailCause.FDN_BLOCKED)716         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_FDN_BLOCKED,
717                 CallFailCause.FDN_BLOCKED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REDIRECTED, CallFailCause.SIP_REDIRECTED)718         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REDIRECTED,
719                 CallFailCause.SIP_REDIRECTED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_REQUEST, CallFailCause.SIP_BAD_REQUEST)720         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_REQUEST,
721                 CallFailCause.SIP_BAD_REQUEST);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_FORBIDDEN, CallFailCause.SIP_FORBIDDEN)722         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_FORBIDDEN,
723                 CallFailCause.SIP_FORBIDDEN);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_FOUND, CallFailCause.SIP_NOT_FOUND)724         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_FOUND,
725                 CallFailCause.SIP_NOT_FOUND);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_SUPPORTED, CallFailCause.SIP_NOT_SUPPORTED)726         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_SUPPORTED,
727                 CallFailCause.SIP_NOT_SUPPORTED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT, CallFailCause.SIP_REQUEST_TIMEOUT)728         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT,
729                 CallFailCause.SIP_REQUEST_TIMEOUT);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE, CallFailCause.SIP_TEMPRARILY_UNAVAILABLE)730         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE,
731                 CallFailCause.SIP_TEMPRARILY_UNAVAILABLE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_ADDRESS, CallFailCause.SIP_BAD_ADDRESS)732         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BAD_ADDRESS,
733                 CallFailCause.SIP_BAD_ADDRESS);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BUSY, CallFailCause.SIP_BUSY)734         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_BUSY,
735                 CallFailCause.SIP_BUSY);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED, CallFailCause.SIP_REQUEST_CANCELLED)736         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_REQUEST_CANCELLED,
737                 CallFailCause.SIP_REQUEST_CANCELLED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE, CallFailCause.SIP_NOT_ACCEPTABLE)738         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE,
739                 CallFailCause.SIP_NOT_ACCEPTABLE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_REACHABLE, CallFailCause.SIP_NOT_REACHABLE)740         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_NOT_REACHABLE,
741                 CallFailCause.SIP_NOT_REACHABLE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_CLIENT_ERROR, CallFailCause.SIP_CLIENT_ERROR)742         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_CLIENT_ERROR,
743                 CallFailCause.SIP_CLIENT_ERROR);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_TRANSACTION_DOES_NOT_EXIST, CallFailCause.SIP_TRANSACTION_DOES_NOT_EXIST)744         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_TRANSACTION_DOES_NOT_EXIST,
745                 CallFailCause.SIP_TRANSACTION_DOES_NOT_EXIST);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_INTERNAL_ERROR, CallFailCause.SIP_SERVER_INTERNAL_ERROR)746         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_INTERNAL_ERROR,
747                 CallFailCause.SIP_SERVER_INTERNAL_ERROR);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE, CallFailCause.SIP_SERVICE_UNAVAILABLE)748         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE,
749                 CallFailCause.SIP_SERVICE_UNAVAILABLE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_TIMEOUT, CallFailCause.SIP_SERVER_TIMEOUT)750         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_TIMEOUT,
751                 CallFailCause.SIP_SERVER_TIMEOUT);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_ERROR, CallFailCause.SIP_SERVER_ERROR)752         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_SERVER_ERROR,
753                 CallFailCause.SIP_SERVER_ERROR);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_USER_REJECTED, CallFailCause.SIP_USER_REJECTED)754         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_USER_REJECTED,
755                 CallFailCause.SIP_USER_REJECTED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_GLOBAL_ERROR, CallFailCause.SIP_GLOBAL_ERROR)756         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SIP_GLOBAL_ERROR,
757                 CallFailCause.SIP_GLOBAL_ERROR);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE, CallFailCause.IMS_EMERGENCY_TEMP_FAILURE)758         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE,
759                 CallFailCause.IMS_EMERGENCY_TEMP_FAILURE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE, CallFailCause.IMS_EMERGENCY_PERM_FAILURE)760         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE,
761                 CallFailCause.IMS_EMERGENCY_PERM_FAILURE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_INIT_FAILED, CallFailCause.MEDIA_INIT_FAILED)762         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_INIT_FAILED,
763                 CallFailCause.MEDIA_INIT_FAILED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NO_DATA, CallFailCause.MEDIA_NO_DATA)764         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NO_DATA,
765                 CallFailCause.MEDIA_NO_DATA);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NOT_ACCEPTABLE, CallFailCause.MEDIA_NOT_ACCEPTABLE)766         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_NOT_ACCEPTABLE,
767                 CallFailCause.MEDIA_NOT_ACCEPTABLE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_UNSPECIFIED, CallFailCause.MEDIA_UNSPECIFIED)768         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MEDIA_UNSPECIFIED,
769                 CallFailCause.MEDIA_UNSPECIFIED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED, CallFailCause.USER_TERMINATED)770         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED,
771                 CallFailCause.USER_TERMINATED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_NOANSWER, CallFailCause.USER_NOANSWER)772         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_NOANSWER,
773                 CallFailCause.USER_NOANSWER);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_IGNORE, CallFailCause.USER_IGNORE)774         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_IGNORE,
775                 CallFailCause.USER_IGNORE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_DECLINE, CallFailCause.USER_DECLINE)776         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_DECLINE,
777                 CallFailCause.USER_DECLINE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOW_BATTERY, CallFailCause.LOW_BATTERY)778         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_LOW_BATTERY,
779                 CallFailCause.LOW_BATTERY);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_BLACKLISTED_CALL_ID, CallFailCause.BLACKLISTED_CALL_ID)780         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_BLACKLISTED_CALL_ID,
781                 CallFailCause.BLACKLISTED_CALL_ID);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE, CallFailCause.USER_TERMINATED_BY_REMOTE)782         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE,
783                 CallFailCause.USER_TERMINATED_BY_REMOTE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NOT_SUPPORTED, CallFailCause.UT_NOT_SUPPORTED)784         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NOT_SUPPORTED,
785                 CallFailCause.UT_NOT_SUPPORTED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE, CallFailCause.UT_SERVICE_UNAVAILABLE)786         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_SERVICE_UNAVAILABLE,
787                 CallFailCause.UT_SERVICE_UNAVAILABLE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_OPERATION_NOT_ALLOWED, CallFailCause.UT_OPERATION_NOT_ALLOWED)788         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_OPERATION_NOT_ALLOWED,
789                 CallFailCause.UT_OPERATION_NOT_ALLOWED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NETWORK_ERROR, CallFailCause.UT_NETWORK_ERROR)790         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_NETWORK_ERROR,
791                 CallFailCause.UT_NETWORK_ERROR);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH, CallFailCause.UT_CB_PASSWORD_MISMATCH)792         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UT_CB_PASSWORD_MISMATCH,
793                 CallFailCause.UT_CB_PASSWORD_MISMATCH);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED, CallFailCause.ECBM_NOT_SUPPORTED)794         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ECBM_NOT_SUPPORTED,
795                 CallFailCause.ECBM_NOT_SUPPORTED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED, CallFailCause.MULTIENDPOINT_NOT_SUPPORTED)796         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MULTIENDPOINT_NOT_SUPPORTED,
797                 CallFailCause.MULTIENDPOINT_NOT_SUPPORTED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE, CallFailCause.CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE)798         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE,
799                 CallFailCause.CALL_DROP_IWLAN_TO_LTE_UNAVAILABLE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE, CallFailCause.ANSWERED_ELSEWHERE)800         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ANSWERED_ELSEWHERE,
801                 CallFailCause.ANSWERED_ELSEWHERE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC, CallFailCause.CALL_PULL_OUT_OF_SYNC)802         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC,
803                 CallFailCause.CALL_PULL_OUT_OF_SYNC);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL, CallFailCause.CALL_PULLED)804         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL,
805                 CallFailCause.CALL_PULLED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_FAILED, CallFailCause.SUPP_SVC_FAILED)806         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_FAILED,
807                 CallFailCause.SUPP_SVC_FAILED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_CANCELLED, CallFailCause.SUPP_SVC_CANCELLED)808         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_CANCELLED,
809                 CallFailCause.SUPP_SVC_CANCELLED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_REINVITE_COLLISION, CallFailCause.SUPP_SVC_REINVITE_COLLISION)810         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_SUPP_SVC_REINVITE_COLLISION,
811                 CallFailCause.SUPP_SVC_REINVITE_COLLISION);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_IWLAN_DPD_FAILURE, CallFailCause.IWLAN_DPD_FAILURE)812         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_IWLAN_DPD_FAILURE,
813                 CallFailCause.IWLAN_DPD_FAILURE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_ESTABLISH_FAILURE, CallFailCause.EPDG_TUNNEL_ESTABLISH_FAILURE)814         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_ESTABLISH_FAILURE,
815                 CallFailCause.EPDG_TUNNEL_ESTABLISH_FAILURE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_REKEY_FAILURE, CallFailCause.EPDG_TUNNEL_REKEY_FAILURE)816         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_REKEY_FAILURE,
817                 CallFailCause.EPDG_TUNNEL_REKEY_FAILURE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_LOST_CONNECTION, CallFailCause.EPDG_TUNNEL_LOST_CONNECTION)818         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_EPDG_TUNNEL_LOST_CONNECTION,
819                 CallFailCause.EPDG_TUNNEL_LOST_CONNECTION);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED, CallFailCause.MAXIMUM_NUMBER_OF_CALLS_REACHED)820         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED,
821                 CallFailCause.MAXIMUM_NUMBER_OF_CALLS_REACHED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE, CallFailCause.REMOTE_CALL_DECLINE)822         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_REMOTE_CALL_DECLINE,
823                 CallFailCause.REMOTE_CALL_DECLINE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_LIMIT_REACHED, CallFailCause.DATA_LIMIT_REACHED)824         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_LIMIT_REACHED,
825                 CallFailCause.DATA_LIMIT_REACHED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_DISABLED, CallFailCause.DATA_DISABLED)826         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_DATA_DISABLED,
827                 CallFailCause.DATA_DISABLED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_WIFI_LOST, CallFailCause.WIFI_LOST)828         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_WIFI_LOST,
829                 CallFailCause.WIFI_LOST);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_OFF, CallFailCause.RADIO_OFF)830         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_OFF,
831                 CallFailCause.RADIO_OFF);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NO_VALID_SIM, CallFailCause.NO_VALID_SIM)832         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NO_VALID_SIM,
833                 CallFailCause.NO_VALID_SIM);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_INTERNAL_ERROR, CallFailCause.RADIO_INTERNAL_ERROR)834         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_INTERNAL_ERROR,
835                 CallFailCause.RADIO_INTERNAL_ERROR);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_RESP_TIMEOUT, CallFailCause.NETWORK_RESP_TIMEOUT)836         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_RESP_TIMEOUT,
837                 CallFailCause.NETWORK_RESP_TIMEOUT);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_REJECT, CallFailCause.NETWORK_REJECT)838         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_REJECT,
839                 CallFailCause.NETWORK_REJECT);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_ACCESS_FAILURE, CallFailCause.RADIO_ACCESS_FAILURE)840         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_ACCESS_FAILURE,
841                 CallFailCause.RADIO_ACCESS_FAILURE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_FAILURE, CallFailCause.RADIO_LINK_FAILURE)842         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_FAILURE,
843                 CallFailCause.RADIO_LINK_FAILURE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_LOST, CallFailCause.RADIO_LINK_LOST)844         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_LINK_LOST,
845                 CallFailCause.RADIO_LINK_LOST);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_UPLINK_FAILURE, CallFailCause.RADIO_UPLINK_FAILURE)846         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_UPLINK_FAILURE,
847                 CallFailCause.RADIO_UPLINK_FAILURE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_SETUP_FAILURE, CallFailCause.RADIO_SETUP_FAILURE)848         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_SETUP_FAILURE,
849                 CallFailCause.RADIO_SETUP_FAILURE);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_NORMAL, CallFailCause.RADIO_RELEASE_NORMAL)850         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_NORMAL,
851                 CallFailCause.RADIO_RELEASE_NORMAL);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_ABNORMAL, CallFailCause.RADIO_RELEASE_ABNORMAL)852         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_RADIO_RELEASE_ABNORMAL,
853                 CallFailCause.RADIO_RELEASE_ABNORMAL);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED, CallFailCause.ACCESS_CLASS_BLOCKED)854         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED,
855                 CallFailCause.ACCESS_CLASS_BLOCKED);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_DETACH, CallFailCause.NETWORK_DETACH)856         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_NETWORK_DETACH,
857                 CallFailCause.NETWORK_DETACH);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UNOBTAINABLE_NUMBER, CallFailCause.UNOBTAINABLE_NUMBER)858         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_UNOBTAINABLE_NUMBER,
859                 CallFailCause.UNOBTAINABLE_NUMBER);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_1, CallFailCause.OEM_CAUSE_1)860         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_1,
861                 CallFailCause.OEM_CAUSE_1);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_2, CallFailCause.OEM_CAUSE_2)862         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_2,
863                 CallFailCause.OEM_CAUSE_2);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_3, CallFailCause.OEM_CAUSE_3)864         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_3,
865                 CallFailCause.OEM_CAUSE_3);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_4, CallFailCause.OEM_CAUSE_4)866         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_4,
867                 CallFailCause.OEM_CAUSE_4);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_5, CallFailCause.OEM_CAUSE_5)868         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_5,
869                 CallFailCause.OEM_CAUSE_5);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_6, CallFailCause.OEM_CAUSE_6)870         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_6,
871                 CallFailCause.OEM_CAUSE_6);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_7, CallFailCause.OEM_CAUSE_7)872         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_7,
873                 CallFailCause.OEM_CAUSE_7);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_8, CallFailCause.OEM_CAUSE_8)874         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_8,
875                 CallFailCause.OEM_CAUSE_8);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_9, CallFailCause.OEM_CAUSE_9)876         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_9,
877                 CallFailCause.OEM_CAUSE_9);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_10, CallFailCause.OEM_CAUSE_10)878         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_10,
879                 CallFailCause.OEM_CAUSE_10);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_11, CallFailCause.OEM_CAUSE_11)880         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_11,
881                 CallFailCause.OEM_CAUSE_11);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_12, CallFailCause.OEM_CAUSE_12)882         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_12,
883                 CallFailCause.OEM_CAUSE_12);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_13, CallFailCause.OEM_CAUSE_13)884         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_13,
885                 CallFailCause.OEM_CAUSE_13);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_14, CallFailCause.OEM_CAUSE_14)886         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_14,
887                 CallFailCause.OEM_CAUSE_14);
PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_15, CallFailCause.OEM_CAUSE_15)888         PRECISE_CAUSE_MAP.append(ImsReasonInfo.CODE_OEM_CAUSE_15,
889                 CallFailCause.OEM_CAUSE_15);
890     }
891 
892     /**
893      * Carrier configuration option which determines whether the carrier wants to inform the user
894      * when a video call is handed over from WIFI to LTE.
895      * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL} for more
896      * information.
897      */
898     private boolean mNotifyHandoverVideoFromWifiToLTE = false;
899 
900     /**
901      * Carrier configuration option which determines whether the carrier wants to inform the user
902      * when a video call is handed over from LTE to WIFI.
903      * See {@link CarrierConfigManager#KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL} for more
904      * information.
905      */
906     private boolean mNotifyHandoverVideoFromLTEToWifi = false;
907 
908     /**
909      * When {@code} false, indicates that no handover from LTE to WIFI has been attempted during the
910      * start of the call.
911      * When {@code true}, indicates that the start of call handover from LTE to WIFI has been
912      * attempted (it may have succeeded or failed).
913      */
914     private boolean mHasAttemptedStartOfCallHandover = false;
915 
916     /**
917      * Carrier configuration option which determines whether the carrier supports the
918      * {@link VideoProfile#STATE_PAUSED} signalling.
919      * See {@link CarrierConfigManager#KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL} for more information.
920      */
921     private boolean mSupportPauseVideo = false;
922 
923     /**
924      * Carrier configuration option which defines a mapping from pairs of
925      * {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()} values to a new
926      * {@code ImsReasonInfo#CODE_*} value.
927      *
928      * See {@link CarrierConfigManager#KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY}.
929      * This ImsReasonInfoKeyPair with this key stating will consider getExtraMessage a match
930      * if the carrier config messages starts with getExtraMessage result.
931      */
932     private Map<ImsReasonInfoKeyPair, Integer> mImsReasonCodeMap = new ArrayMap<>();
933 
934 
935     /**
936       * Carrier configuration option which specifies how the carrier handles USSD request.
937       * See {@link CarrierConfigManager#KEY_CARRIER_USSD_METHOD_INT} for more information.
938       */
939     private int mUssdMethod = USSD_OVER_CS_PREFERRED;
940 
941     /**
942      * TODO: Remove this code; it is a workaround.
943      * When {@code true}, forces {@link ImsManager#updateImsServiceConfig} to
944      * be called when an ongoing video call is disconnected.  In some cases, where video pause is
945      * supported by the carrier, when {@link #onDataEnabledChanged(boolean, int)} reports that data
946      * has been disabled we will pause the video rather than disconnecting the call.  When this
947      * happens we need to prevent the IMS service config from being updated, as this will cause VT
948      * to be disabled mid-call, resulting in an inability to un-pause the video.
949      */
950     private boolean mShouldUpdateImsConfigOnDisconnect = false;
951 
952     private Pair<Boolean, Integer> mPendingSilentRedialInfo = null;
953 
954     /**
955      * Default implementation for retrieving shared preferences; uses the actual PreferencesManager.
956      */
957     private SharedPreferenceProxy mSharedPreferenceProxy = (Context context) -> {
958         return PreferenceManager.getDefaultSharedPreferences(context);
959     };
960 
961     private Runnable mConnectorRunnable = new Runnable() {
962         @Override
963         public void run() {
964             mImsManagerConnector.connect();
965         }
966     };
967 
968     // TODO: make @NonNull after removing DataEnabledSettings
969     private DataSettingsManager.DataSettingsManagerCallback mSettingsCallback;
970 
971     /**
972      * Allows the FeatureConnector used to be swapped for easier testing.
973      */
974     @VisibleForTesting
975     public interface ConnectorFactory {
976         /**
977          * Create a FeatureConnector for this class to use to connect to an ImsManager.
978          */
create(Context context, int phoneId, String logPrefix, FeatureConnector.Listener<ImsManager> listener, Executor executor)979         FeatureConnector<ImsManager> create(Context context, int phoneId,
980                 String logPrefix, FeatureConnector.Listener<ImsManager> listener,
981                 Executor executor);
982     }
983     private final ConnectorFactory mConnectorFactory;
984     private final FeatureConnector<ImsManager> mImsManagerConnector;
985 
986     // Used exclusively for IMS Registration related events for logging.
987     private final LocalLog mRegLocalLog = new LocalLog(64);
988     // Used for important operational related events for logging.
989     private final LocalLog mOperationLocalLog = new LocalLog(64);
990 
991     /**
992      * Container to ease passing around a tuple of two objects. This object provides a sensible
993      * implementation of equals(), returning true/false using equals() for one object (Integer)
994      * and startsWith() for another object (String). Also the startsWith() in this equals() method
995      * will return true for A.startsWith(B) if B.second starts with A.second.
996      */
997     private static class ImsReasonInfoKeyPair extends Pair<Integer, String> {
998 
999         /**
1000          * Constructor for a ImsReasonInfoKeyPair.
1001          *
1002          * @param first Integer in the ImsReasonInfoKeyPair
1003          * @param second String in the ImsReasonInfoKeyPair
1004          */
ImsReasonInfoKeyPair(Integer first, String second)1005         private ImsReasonInfoKeyPair(Integer first, String second) {
1006             super(first, second);
1007         }
1008 
1009         /**
1010          * Checks the two objects for equality by delegating to their respective
1011          * {@link Object#equals(Object)} methods.
1012          *
1013          * @param o the {@link com.android.internal.telephony.imsphone.ImsReasonInfoKeyPair} to
1014          *         which this one is to be checked for equality
1015          * @return true if the underlying objects of the ImsReasonInfoKeyPair are
1016          * considered equal and startsWith
1017          */
1018         @Override
equals(@ullable Object o)1019         public boolean equals(@Nullable Object o) {
1020             if (!(o instanceof ImsReasonInfoKeyPair)) {
1021                 return false;
1022             }
1023             ImsReasonInfoKeyPair p = (ImsReasonInfoKeyPair) o;
1024 
1025             return Objects.equals(p.first, first)
1026                     && Objects.toString(second).startsWith(Objects.toString(p.second));
1027         }
1028 
1029         /**
1030          * Compute a hash code using the hash code of the Integer key
1031          *
1032          * @return a hashcode of the first
1033          */
1034         @Override
hashCode()1035         public int hashCode() {
1036             return (first == null ? 0 : first.hashCode());
1037         }
1038     }
1039     //***** Events
1040 
1041 
1042     //***** Constructors
ImsPhoneCallTracker(ImsPhone phone, ConnectorFactory factory)1043     public ImsPhoneCallTracker(ImsPhone phone, ConnectorFactory factory) {
1044         this(phone, factory, phone.getContext().getMainExecutor());
1045     }
1046 
1047     @VisibleForTesting
ImsPhoneCallTracker(ImsPhone phone, ConnectorFactory factory, Executor executor)1048     public ImsPhoneCallTracker(ImsPhone phone, ConnectorFactory factory, Executor executor) {
1049         this.mPhone = phone;
1050         mConnectorFactory = factory;
1051         if (executor != null) {
1052             mExecutor = executor;
1053         }
1054 
1055         mMetrics = TelephonyMetrics.getInstance();
1056 
1057         IntentFilter intentfilter = new IntentFilter();
1058         intentfilter.addAction(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED);
1059         intentfilter.addAction(TelecomManager.ACTION_DEFAULT_DIALER_CHANGED);
1060         mPhone.getContext().registerReceiver(mReceiver, intentfilter);
1061         updateCarrierConfiguration(mPhone.getSubId(), getCarrierConfigBundle(mPhone.getSubId()));
1062 
1063         if (mPhone.getDefaultPhone().isUsingNewDataStack()) {
1064             mSettingsCallback = new DataSettingsManager.DataSettingsManagerCallback(this::post) {
1065                     @Override
1066                     public void onDataEnabledChanged(boolean enabled,
1067                             @TelephonyManager.DataEnabledChangedReason int reason,
1068                             @NonNull String callingPackage) {
1069                         int internalReason;
1070                         switch (reason) {
1071                             case TelephonyManager.DATA_ENABLED_REASON_USER:
1072                                 internalReason = DataEnabledSettings.REASON_USER_DATA_ENABLED;
1073                                 break;
1074                             case TelephonyManager.DATA_ENABLED_REASON_POLICY:
1075                                 internalReason = DataEnabledSettings.REASON_POLICY_DATA_ENABLED;
1076                                 break;
1077                             case TelephonyManager.DATA_ENABLED_REASON_CARRIER:
1078                                 internalReason = DataEnabledSettings.REASON_DATA_ENABLED_BY_CARRIER;
1079                                 break;
1080                             case TelephonyManager.DATA_ENABLED_REASON_THERMAL:
1081                                 internalReason = DataEnabledSettings.REASON_THERMAL_DATA_ENABLED;
1082                                 break;
1083                             case TelephonyManager.DATA_ENABLED_REASON_OVERRIDE:
1084                                 internalReason = DataEnabledSettings.REASON_OVERRIDE_RULE_CHANGED;
1085                                 break;
1086                             default:
1087                                 internalReason = DataEnabledSettings.REASON_INTERNAL_DATA_ENABLED;
1088                         }
1089                         ImsPhoneCallTracker.this.onDataEnabledChanged(enabled, internalReason);
1090                     }};
1091             mPhone.getDefaultPhone().getDataSettingsManager().registerCallback(mSettingsCallback);
1092         } else {
1093             mPhone.getDefaultPhone().getDataEnabledSettings().registerForDataEnabledChanged(
1094                     this, EVENT_DATA_ENABLED_CHANGED, null);
1095         }
1096 
1097         final TelecomManager telecomManager =
1098                 (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
1099         mDefaultDialerUid.set(
1100                 getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage()));
1101 
1102         long currentTime = SystemClock.elapsedRealtime();
1103         mVtDataUsageSnapshot = new NetworkStats(currentTime, 1);
1104         mVtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
1105         final NetworkStatsManager statsManager =
1106                 (NetworkStatsManager) mPhone.getContext().getSystemService(
1107                         Context.NETWORK_STATS_SERVICE);
1108         statsManager.registerNetworkStatsProvider(LOG_TAG, mVtDataUsageProvider);
1109 
1110         mImsManagerConnector = mConnectorFactory.create(mPhone.getContext(), mPhone.getPhoneId(),
1111                 LOG_TAG, new FeatureConnector.Listener<ImsManager>() {
1112                     public void connectionReady(ImsManager manager, int subId) throws ImsException {
1113                         mImsManager = manager;
1114                         log("connectionReady for subId = " + subId);
1115                         startListeningForCalls(subId);
1116                     }
1117 
1118                     @Override
1119                     public void connectionUnavailable(int reason) {
1120                         logi("connectionUnavailable: " + reason);
1121                         if (reason == FeatureConnector.UNAVAILABLE_REASON_SERVER_UNAVAILABLE) {
1122                             postDelayed(mConnectorRunnable, CONNECTOR_RETRY_DELAY_MS);
1123                         }
1124                         stopListeningForCalls();
1125                     }
1126                 }, executor);
1127         // It can take some time for ITelephony to get published, so defer connecting.
1128         post(mConnectorRunnable);
1129     }
1130 
1131     /**
1132      * Test-only method used to mock out access to the shared preferences through the
1133      * {@link PreferenceManager}.
1134      * @param sharedPreferenceProxy
1135      */
1136     @VisibleForTesting
setSharedPreferenceProxy(SharedPreferenceProxy sharedPreferenceProxy)1137     public void setSharedPreferenceProxy(SharedPreferenceProxy sharedPreferenceProxy) {
1138         mSharedPreferenceProxy = sharedPreferenceProxy;
1139     }
1140 
getPackageUid(Context context, String pkg)1141     private int getPackageUid(Context context, String pkg) {
1142         if (pkg == null) {
1143             return NetworkStats.UID_ALL;
1144         }
1145 
1146         // Initialize to UID_ALL so at least it can be counted to overall data usage if
1147         // the dialer's package uid is not available.
1148         int uid = NetworkStats.UID_ALL;
1149         try {
1150             uid = context.getPackageManager().getPackageUid(pkg, 0);
1151         } catch (PackageManager.NameNotFoundException e) {
1152             loge("Cannot find package uid. pkg = " + pkg);
1153         }
1154         return uid;
1155     }
1156 
1157     @VisibleForTesting
startListeningForCalls(int subId)1158     public void startListeningForCalls(int subId) throws ImsException {
1159         log("startListeningForCalls");
1160         mOperationLocalLog.log("startListeningForCalls - Connecting to ImsService");
1161         ImsExternalCallTracker externalCallTracker = mPhone.getExternalCallTracker();
1162         ImsExternalCallTracker.ExternalCallStateListener externalCallStateListener =
1163                 externalCallTracker != null
1164                         ? externalCallTracker.getExternalCallStateListener() : null;
1165 
1166         mImsManager.open(mMmTelFeatureListener, mPhone.getImsEcbmStateListener(),
1167                 externalCallStateListener);
1168         mImsManager.addRegistrationCallback(mPhone.getImsMmTelRegistrationCallback(), this::post);
1169         mImsManager.addCapabilitiesCallback(mImsCapabilityCallback, this::post);
1170 
1171         ImsManager.setImsStatsCallback(mPhone.getPhoneId(), mImsStatsCallback);
1172 
1173         mImsManager.getConfigInterface().addConfigCallback(mConfigCallback);
1174 
1175         if (mPhone.isInEcm()) {
1176             // Call exit ECBM which will invoke onECBMExited
1177             mPhone.exitEmergencyCallbackMode();
1178         }
1179         int mPreferredTtyMode = Settings.Secure.getInt(
1180                 mPhone.getContext().getContentResolver(),
1181                 Settings.Secure.PREFERRED_TTY_MODE,
1182                 Phone.TTY_MODE_OFF);
1183         mImsManager.setUiTTYMode(mPhone.getContext(), mPreferredTtyMode, null);
1184 
1185         // Set UT interface listener to receive UT indications & keep track of the interface so the
1186         // handler reference can be cleared.
1187         mUtInterface = getUtInterface();
1188         if (mUtInterface != null) {
1189             mUtInterface.registerForSuppServiceIndication(this, EVENT_SUPP_SERVICE_INDICATION,
1190                     null);
1191         }
1192 
1193         if (mCarrierConfigForSubId != null && mCarrierConfigForSubId.first == subId) {
1194             // The carrier configuration was received by CarrierConfigManager before the indication
1195             // that the ImsService was connected or ImsService has restarted and we need to re-apply
1196             // the configuration.
1197             updateCarrierConfiguration(subId, mCarrierConfigForSubId.second);
1198         } else {
1199             log("startListeningForCalls - waiting for the first carrier config indication for this "
1200                     + "subscription");
1201         }
1202         // For compatibility with apps that still use deprecated intent
1203         sendImsServiceStateIntent(ImsManager.ACTION_IMS_SERVICE_UP);
1204         mCurrentlyConnectedSubId = Optional.of(subId);
1205     }
1206 
1207     /**
1208      * Configures RTP header extension types used during SDP negotiation.
1209      */
maybeConfigureRtpHeaderExtensions()1210     private void maybeConfigureRtpHeaderExtensions() {
1211         // Where device to device communication is available, ensure that the
1212         // supported RTP header extension types defined in {@link RtpTransport} are
1213         // set as the offered RTP header extensions for this device.
1214         if (mDeviceToDeviceForceEnabled
1215                 || (mConfig != null && mConfig.isD2DCommunicationSupported
1216                 && mSupportD2DUsingRtp)) {
1217             ArraySet<RtpHeaderExtensionType> types = new ArraySet<>();
1218             if (mSupportSdpForRtpHeaderExtensions) {
1219                 types.add(RtpTransport.CALL_STATE_RTP_HEADER_EXTENSION_TYPE);
1220                 types.add(RtpTransport.DEVICE_STATE_RTP_HEADER_EXTENSION_TYPE);
1221                 logi("maybeConfigureRtpHeaderExtensions: set offered RTP header extension types");
1222 
1223             } else {
1224                 logi("maybeConfigureRtpHeaderExtensions: SDP negotiation not supported; not "
1225                         + "setting offered RTP header extension types");
1226             }
1227             try {
1228                 mImsManager.setOfferedRtpHeaderExtensionTypes(types);
1229             } catch (ImsException e) {
1230                 loge("maybeConfigureRtpHeaderExtensions: failed to set extensions; " + e);
1231             }
1232         }
1233     }
1234 
1235     /**
1236      * Used via the telephony shell command to force D2D to be enabled.
1237      * @param isEnabled {@code true} if D2D is force enabled.
1238      */
setDeviceToDeviceForceEnabled(boolean isEnabled)1239     public void setDeviceToDeviceForceEnabled(boolean isEnabled) {
1240         mDeviceToDeviceForceEnabled = isEnabled;
1241         maybeConfigureRtpHeaderExtensions();
1242     }
1243 
stopListeningForCalls()1244     private void stopListeningForCalls() {
1245         log("stopListeningForCalls");
1246         mOperationLocalLog.log("stopListeningForCalls - Disconnecting from ImsService");
1247         // Only close on valid session.
1248         if (mImsManager != null) {
1249             mImsManager.removeRegistrationListener(mPhone.getImsMmTelRegistrationCallback());
1250             mImsManager.removeCapabilitiesCallback(mImsCapabilityCallback);
1251             try {
1252                 ImsManager.setImsStatsCallback(mPhone.getPhoneId(), null);
1253                 mImsManager.getConfigInterface().removeConfigCallback(mConfigCallback.getBinder());
1254             } catch (ImsException e) {
1255                 Log.w(LOG_TAG, "stopListeningForCalls: unable to remove config callback.");
1256             }
1257             // Will release other listeners for MMTEL/ECBM/UT/MultiEndpoint Indications set in #open
1258             mImsManager.close();
1259         }
1260         if (mUtInterface != null) {
1261             mUtInterface.unregisterForSuppServiceIndication(this);
1262             mUtInterface = null;
1263         }
1264         mCurrentlyConnectedSubId = Optional.empty();
1265         resetImsCapabilities();
1266         hangupAllOrphanedConnections(DisconnectCause.LOST_SIGNAL);
1267         // For compatibility with apps that still use deprecated intent
1268         sendImsServiceStateIntent(ImsManager.ACTION_IMS_SERVICE_DOWN);
1269     }
1270 
1271     /**
1272      * Hang up all ongoing connections in the case that the ImsService has been disconnected and the
1273      * existing calls have been orphaned. This method assumes that there is no connection to the
1274      * ImsService and DOES NOT try to terminate the connections on the service side before
1275      * disconnecting here, as it assumes they have already been disconnected when we lost the
1276      * connection to the ImsService.
1277      */
1278     @VisibleForTesting
hangupAllOrphanedConnections(int disconnectCause)1279     public void hangupAllOrphanedConnections(int disconnectCause) {
1280         Log.w(LOG_TAG, "hangupAllOngoingConnections called for cause " + disconnectCause);
1281         // Send a call terminate request to all available connections.
1282         // In the ImsPhoneCallTrackerTest, when the hangup() of the connection call,
1283         // onCallTerminated() is called immediately and the connection is removed.
1284         // As a result, an IndexOutOfBoundsException is thrown.
1285         // This is why it counts backwards.
1286         int size = getConnections().size();
1287         for (int index = size - 1; index > -1; index--) {
1288             try {
1289                 getConnections().get(index).hangup();
1290             } catch (CallStateException e) {
1291                 loge("Failed to disconnet call...");
1292             }
1293         }
1294         // Move connections to disconnected and notify the reason why.
1295         for (ImsPhoneConnection connection : mConnections) {
1296             connection.update(connection.getImsCall(), ImsPhoneCall.State.DISCONNECTED);
1297             connection.onDisconnect(disconnectCause);
1298             connection.getCall().detach(connection);
1299         }
1300         mConnections.clear();
1301         // Pending MO was added to mConnections previously, so it has already been disconnected
1302         // above. Remove all references to it.
1303         mPendingMO = null;
1304         updatePhoneState();
1305     }
1306 
1307     /**
1308      * Requests modem to hang up all connections.
1309      */
hangupAllConnections()1310     public void hangupAllConnections() {
1311         getConnections().stream().forEach(c -> {
1312             logi("Disconnecting callId = " + c.getTelecomCallId());
1313             try {
1314                 c.hangup();
1315             } catch (CallStateException e) {
1316                 loge("Failed to disconnet call...");
1317             }
1318         });
1319     }
1320 
sendImsServiceStateIntent(String intentAction)1321     private void sendImsServiceStateIntent(String intentAction) {
1322         Intent intent = new Intent(intentAction);
1323         intent.putExtra(ImsManager.EXTRA_PHONE_ID, mPhone.getPhoneId());
1324         if (mPhone != null && mPhone.getContext() != null) {
1325             mPhone.getContext().sendBroadcast(intent);
1326         }
1327     }
1328 
dispose()1329     public void dispose() {
1330         if (DBG) log("dispose");
1331         mRingingCall.dispose();
1332         mBackgroundCall.dispose();
1333         mForegroundCall.dispose();
1334         mHandoverCall.dispose();
1335 
1336         clearDisconnected();
1337         mPhone.getContext().unregisterReceiver(mReceiver);
1338         if (mPhone.getDefaultPhone().isUsingNewDataStack()) {
1339             mPhone.getDefaultPhone().getDataSettingsManager().unregisterCallback(mSettingsCallback);
1340         } else {
1341             mPhone.getDefaultPhone().getDataEnabledSettings().unregisterForDataEnabledChanged(this);
1342         }
1343         mImsManagerConnector.disconnect();
1344 
1345         final NetworkStatsManager statsManager =
1346                 (NetworkStatsManager) mPhone.getContext().getSystemService(
1347                         Context.NETWORK_STATS_SERVICE);
1348         statsManager.unregisterNetworkStatsProvider(mVtDataUsageProvider);
1349     }
1350 
1351     @Override
finalize()1352     protected void finalize() {
1353         log("ImsPhoneCallTracker finalized");
1354     }
1355 
1356     //***** Instance Methods
1357 
1358     //***** Public Methods
1359     @Override
registerForVoiceCallStarted(Handler h, int what, Object obj)1360     public void registerForVoiceCallStarted(Handler h, int what, Object obj) {
1361         Registrant r = new Registrant(h, what, obj);
1362         mVoiceCallStartedRegistrants.add(r);
1363     }
1364 
1365     @Override
unregisterForVoiceCallStarted(Handler h)1366     public void unregisterForVoiceCallStarted(Handler h) {
1367         mVoiceCallStartedRegistrants.remove(h);
1368     }
1369 
1370     @Override
registerForVoiceCallEnded(Handler h, int what, Object obj)1371     public void registerForVoiceCallEnded(Handler h, int what, Object obj) {
1372         Registrant r = new Registrant(h, what, obj);
1373         mVoiceCallEndedRegistrants.add(r);
1374     }
1375 
1376     @Override
unregisterForVoiceCallEnded(Handler h)1377     public void unregisterForVoiceCallEnded(Handler h) {
1378         mVoiceCallEndedRegistrants.remove(h);
1379     }
1380 
getClirMode()1381     public int getClirMode() {
1382         if (mSharedPreferenceProxy != null && mPhone.getDefaultPhone() != null) {
1383             SharedPreferences sp = mSharedPreferenceProxy.getDefaultSharedPreferences(
1384                     mPhone.getContext());
1385             return sp.getInt(Phone.CLIR_KEY + mPhone.getSubId(),
1386                     CommandsInterface.CLIR_DEFAULT);
1387         } else {
1388             loge("dial; could not get default CLIR mode.");
1389             return CommandsInterface.CLIR_DEFAULT;
1390         }
1391     }
1392 
prepareForDialing(ImsPhone.ImsDialArgs dialArgs)1393     private boolean prepareForDialing(ImsPhone.ImsDialArgs dialArgs) throws CallStateException {
1394         boolean holdBeforeDial = false;
1395         // note that this triggers call state changed notif
1396         clearDisconnected();
1397         if (mImsManager == null) {
1398             throw new CallStateException("service not available");
1399         }
1400         // See if there are any issues which preclude placing a call; throw a CallStateException
1401         // if there is.
1402         checkForDialIssues();
1403         int videoState = dialArgs.videoState;
1404         if (!canAddVideoCallDuringImsAudioCall(videoState)) {
1405             throw new CallStateException("cannot dial in current state");
1406         }
1407 
1408         // The new call must be assigned to the foreground call.
1409         // That call must be idle, so place anything that's
1410         // there on hold
1411         if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
1412             if (mBackgroundCall.getState() != ImsPhoneCall.State.IDLE) {
1413                 //we should have failed in checkForDialIssues above before we get here
1414                 throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS,
1415                         "Already too many ongoing calls.");
1416             }
1417             // foreground call is empty for the newly dialed connection
1418             holdBeforeDial = true;
1419             mPendingCallVideoState = videoState;
1420             mPendingIntentExtras = dialArgs.intentExtras;
1421             holdActiveCallForPendingMo();
1422         }
1423 
1424         ImsPhoneCall.State fgState = ImsPhoneCall.State.IDLE;
1425         ImsPhoneCall.State bgState = ImsPhoneCall.State.IDLE;
1426 
1427         synchronized (mSyncHold) {
1428             if (holdBeforeDial) {
1429                 fgState = mForegroundCall.getState();
1430                 bgState = mBackgroundCall.getState();
1431                 //holding foreground call failed
1432                 if (fgState == ImsPhoneCall.State.ACTIVE) {
1433                     throw new CallStateException("cannot dial in current state");
1434                 }
1435                 //holding foreground call succeeded
1436                 if (bgState == ImsPhoneCall.State.HOLDING) {
1437                     holdBeforeDial = false;
1438                 }
1439             }
1440         }
1441         return holdBeforeDial;
1442     }
1443 
startConference(String[] participantsToDial, ImsPhone.ImsDialArgs dialArgs)1444     public Connection startConference(String[] participantsToDial, ImsPhone.ImsDialArgs dialArgs)
1445             throws CallStateException {
1446 
1447         int clirMode = dialArgs.clirMode;
1448         int videoState = dialArgs.videoState;
1449 
1450         if (DBG) log("dial clirMode=" + clirMode);
1451         boolean holdBeforeDial = prepareForDialing(dialArgs);
1452 
1453         mClirMode = clirMode;
1454         ImsPhoneConnection pendingConnection;
1455         synchronized (mSyncHold) {
1456             mLastDialArgs = dialArgs;
1457             pendingConnection = new ImsPhoneConnection(mPhone,
1458                     participantsToDial, this, mForegroundCall,
1459                     false);
1460             // Don't rely on the mPendingMO in this method; if the modem calls back through
1461             // onCallProgressing, we'll end up nulling out mPendingMO, which means that
1462             // TelephonyConnectionService would treat this call as an MMI code, which it is not,
1463             // which would mean that the MMI code dialog would crash.
1464             mPendingMO = pendingConnection;
1465             pendingConnection.setVideoState(videoState);
1466             if (dialArgs.rttTextStream != null) {
1467                 log("startConference: setting RTT stream on mPendingMO");
1468                 pendingConnection.setCurrentRttTextStream(dialArgs.rttTextStream);
1469             }
1470         }
1471         addConnection(pendingConnection);
1472 
1473         if (!holdBeforeDial) {
1474             dialInternal(pendingConnection, clirMode, videoState, dialArgs.intentExtras);
1475         }
1476 
1477         updatePhoneState();
1478         mPhone.notifyPreciseCallStateChanged();
1479 
1480         return pendingConnection;
1481     }
1482 
1483     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
dial(String dialString, int videoState, Bundle intentExtras)1484     public Connection dial(String dialString, int videoState, Bundle intentExtras) throws
1485             CallStateException {
1486         ImsPhone.ImsDialArgs dialArgs =  new ImsPhone.ImsDialArgs.Builder()
1487                 .setIntentExtras(intentExtras)
1488                 .setVideoState(videoState)
1489                 .setClirMode(getClirMode())
1490                 .build();
1491         return dial(dialString, dialArgs);
1492     }
1493 
dial(String dialString, ImsPhone.ImsDialArgs dialArgs)1494     public synchronized Connection dial(String dialString, ImsPhone.ImsDialArgs dialArgs)
1495             throws CallStateException {
1496         boolean isPhoneInEcmMode = isPhoneInEcbMode();
1497         boolean isEmergencyNumber = dialArgs.isEmergency;
1498         boolean isWpsCall = dialArgs.isWpsCall;
1499 
1500         if (!shouldNumberBePlacedOnIms(isEmergencyNumber, dialString)) {
1501             Rlog.i(LOG_TAG, "dial: shouldNumberBePlacedOnIms = false");
1502             mOperationLocalLog.log("dial: shouldNumberBePlacedOnIms = false");
1503             throw new CallStateException(CS_FALLBACK);
1504         }
1505 
1506         int clirMode = dialArgs.clirMode;
1507         int videoState = dialArgs.videoState;
1508 
1509         if (DBG) log("dial clirMode=" + clirMode);
1510         String origNumber = dialString;
1511         if (isEmergencyNumber) {
1512             clirMode = CommandsInterface.CLIR_SUPPRESSION;
1513             if (DBG) log("dial emergency call, set clirModIe=" + clirMode);
1514         } else {
1515             dialString = convertNumberIfNecessary(mPhone, dialString);
1516         }
1517 
1518         mClirMode = clirMode;
1519         boolean holdBeforeDial = prepareForDialing(dialArgs);
1520 
1521         if (isPhoneInEcmMode && isEmergencyNumber) {
1522             mPhone.handleTimerInEmergencyCallbackMode(ImsPhone.CANCEL_ECM_TIMER);
1523         }
1524 
1525         // If the call is to an emergency number and the carrier does not support video emergency
1526         // calls, dial as an audio-only call.
1527         if (isEmergencyNumber && VideoProfile.isVideo(videoState) &&
1528                 !mAllowEmergencyVideoCalls) {
1529             loge("dial: carrier does not support video emergency calls; downgrade to audio-only");
1530             videoState = VideoProfile.STATE_AUDIO_ONLY;
1531         }
1532 
1533         // Cache the video state for pending MO call.
1534         mPendingCallVideoState = videoState;
1535 
1536         synchronized (mSyncHold) {
1537             mLastDialString = dialString;
1538             mLastDialArgs = dialArgs;
1539             mPendingMO = new ImsPhoneConnection(mPhone, dialString, this, mForegroundCall,
1540                     isEmergencyNumber, isWpsCall);
1541             mOperationLocalLog.log("dial requested. connId=" + System.identityHashCode(mPendingMO));
1542             if (isEmergencyNumber && dialArgs != null && dialArgs.intentExtras != null) {
1543                 Rlog.i(LOG_TAG, "dial ims emergency dialer: " + dialArgs.intentExtras.getBoolean(
1544                         TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL));
1545                 mPendingMO.setHasKnownUserIntentEmergency(dialArgs.intentExtras.getBoolean(
1546                         TelecomManager.EXTRA_IS_USER_INTENT_EMERGENCY_CALL));
1547             }
1548             mPendingMO.setVideoState(videoState);
1549             if (dialArgs.rttTextStream != null) {
1550                 log("dial: setting RTT stream on mPendingMO");
1551                 mPendingMO.setCurrentRttTextStream(dialArgs.rttTextStream);
1552             }
1553         }
1554         addConnection(mPendingMO);
1555 
1556         if (!holdBeforeDial) {
1557             if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) {
1558                 dialInternal(mPendingMO, clirMode, videoState, dialArgs.retryCallFailCause,
1559                         dialArgs.retryCallFailNetworkType, dialArgs.intentExtras);
1560             } else {
1561                 try {
1562                     getEcbmInterface().exitEmergencyCallbackMode();
1563                 } catch (ImsException e) {
1564                     e.printStackTrace();
1565                     throw new CallStateException("service not available");
1566                 }
1567                 mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
1568                 pendingCallClirMode = clirMode;
1569                 mPendingCallVideoState = videoState;
1570                 mPendingIntentExtras = dialArgs.intentExtras;
1571                 pendingCallInEcm = true;
1572             }
1573         }
1574 
1575         if (mNumberConverted) {
1576             mPendingMO.restoreDialedNumberAfterConversion(origNumber);
1577             mNumberConverted = false;
1578         }
1579 
1580         updatePhoneState();
1581         mPhone.notifyPreciseCallStateChanged();
1582 
1583         return mPendingMO;
1584     }
1585 
isImsServiceReady()1586     boolean isImsServiceReady() {
1587         if (mImsManager == null) {
1588             return false;
1589         }
1590 
1591         return mImsManager.isServiceReady();
1592     }
1593 
shouldNumberBePlacedOnIms(boolean isEmergency, String number)1594     private boolean shouldNumberBePlacedOnIms(boolean isEmergency, String number) {
1595         int processCallResult;
1596         try {
1597             if (mImsManager != null) {
1598                 processCallResult = mImsManager.shouldProcessCall(isEmergency,
1599                         new String[]{number});
1600                 Rlog.i(LOG_TAG, "shouldProcessCall: number: " + Rlog.pii(LOG_TAG, number)
1601                         + ", result: " + processCallResult);
1602             } else {
1603                 Rlog.w(LOG_TAG, "ImsManager unavailable, shouldProcessCall returning false.");
1604                 return false;
1605             }
1606         } catch (ImsException e) {
1607             Rlog.w(LOG_TAG, "ImsService unavailable, shouldProcessCall returning false.");
1608             return false;
1609         }
1610         switch(processCallResult) {
1611             case MmTelFeature.PROCESS_CALL_IMS: {
1612                 // The ImsService wishes to place the call over IMS
1613                 return true;
1614             }
1615             case MmTelFeature.PROCESS_CALL_CSFB: {
1616                 Rlog.i(LOG_TAG, "shouldProcessCall: place over CSFB instead.");
1617                 return false;
1618             }
1619             default: {
1620                 Rlog.w(LOG_TAG, "shouldProcessCall returned unknown result.");
1621                 return false;
1622             }
1623         }
1624     }
1625 
1626     /**
1627      * Caches frequently used carrier configuration items locally and notifies ImsService of new
1628      * configuration if the subId is valid (there is an active sub ID loaded).
1629      *
1630      * @param subId The sub id to use to update configuration, may be invalid if a SIM has been
1631      *              removed.
1632      */
updateCarrierConfiguration(int subId, PersistableBundle carrierConfig)1633     private void updateCarrierConfiguration(int subId, PersistableBundle carrierConfig) {
1634         // start by assuming the carrier config is not loaded for the provided subscription.
1635         mCarrierConfigLoadedForSubscription = false;
1636 
1637         if (carrierConfig == null) {
1638             loge("updateCarrierConfiguration: carrier config is null, skipping.");
1639             return;
1640         }
1641 
1642         // Ensure the local cache is up to date first (including default config for no SIM case) in
1643         // ImsPhoneCallTracker to ensure we do not carry over settings from the previously inserted
1644         // SIM for things like emergency calling.
1645         updateCarrierConfigCache(carrierConfig);
1646         log("updateCarrierConfiguration: Updating mAllowEmergencyVideoCalls = "
1647                 + mAllowEmergencyVideoCalls);
1648         // Check for changes due to carrier config.
1649         maybeConfigureRtpHeaderExtensions();
1650 
1651         if (!SubscriptionController.getInstance().isActiveSubId(subId)) {
1652             loge("updateCarrierConfiguration: skipping notification to ImsService, non"
1653                     + "active subId = " + subId);
1654             return;
1655         }
1656 
1657         Phone defaultPhone = getPhone().getDefaultPhone();
1658         if (defaultPhone != null && defaultPhone.getIccCard() != null) {
1659             IccCardConstants.State state = defaultPhone.getIccCard().getState();
1660             // Bypass until PIN/PUK lock is removed as to ensure that we do not push a config down
1661             // when the device is still locked. A CARRIER_CONFIG_CHANGED indication will be sent
1662             // once the device moves to ready.
1663             if (state != null && (!state.iccCardExist() || state.isPinLocked())) {
1664                 loge("updateCarrierConfiguration: card state is not ready, skipping "
1665                         + "notification to ImsService. State= " + state);
1666                 return;
1667             }
1668         }
1669 
1670         if (!CarrierConfigManager.isConfigForIdentifiedCarrier(carrierConfig)) {
1671             logi("updateCarrierConfiguration: Empty or default carrier config, skipping "
1672                     + "notification to ImsService.");
1673             return;
1674         }
1675 
1676         // Only update the ImsService configurations for the case where a new subscription has been
1677         // loaded and is active.
1678         logi("updateCarrierConfiguration: Updating ImsService configs.");
1679         mCarrierConfigLoadedForSubscription = true;
1680         updateImsServiceConfig();
1681     }
1682 
1683     /**
1684      * Updates the local carrier config cache from a bundle obtained from the carrier config
1685      * manager.  Also supports unit testing by injecting configuration at test time.
1686      * @param carrierConfig The config bundle.
1687      */
1688     @VisibleForTesting
updateCarrierConfigCache(PersistableBundle carrierConfig)1689     public void updateCarrierConfigCache(PersistableBundle carrierConfig) {
1690         mAllowEmergencyVideoCalls =
1691                 carrierConfig.getBoolean(CarrierConfigManager.KEY_ALLOW_EMERGENCY_VIDEO_CALLS_BOOL);
1692         mTreatDowngradedVideoCallsAsVideoCalls =
1693                 carrierConfig.getBoolean(
1694                         CarrierConfigManager.KEY_TREAT_DOWNGRADED_VIDEO_CALLS_AS_VIDEO_CALLS_BOOL);
1695         mDropVideoCallWhenAnsweringAudioCall =
1696                 carrierConfig.getBoolean(
1697                         CarrierConfigManager.KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL);
1698         mAllowAddCallDuringVideoCall =
1699                 carrierConfig.getBoolean(
1700                         CarrierConfigManager.KEY_ALLOW_ADD_CALL_DURING_VIDEO_CALL_BOOL);
1701         mAllowHoldingVideoCall =
1702                 carrierConfig.getBoolean(
1703                         CarrierConfigManager.KEY_ALLOW_HOLD_VIDEO_CALL_BOOL);
1704         mNotifyVtHandoverToWifiFail = carrierConfig.getBoolean(
1705                 CarrierConfigManager.KEY_NOTIFY_VT_HANDOVER_TO_WIFI_FAILURE_BOOL);
1706         mSupportDowngradeVtToAudio = carrierConfig.getBoolean(
1707                 CarrierConfigManager.KEY_SUPPORT_DOWNGRADE_VT_TO_AUDIO_BOOL);
1708         mNotifyHandoverVideoFromWifiToLTE = carrierConfig.getBoolean(
1709                 CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_WIFI_TO_LTE_BOOL);
1710         mNotifyHandoverVideoFromLTEToWifi = carrierConfig.getBoolean(
1711                 CarrierConfigManager.KEY_NOTIFY_HANDOVER_VIDEO_FROM_LTE_TO_WIFI_BOOL);
1712         mIgnoreDataEnabledChangedForVideoCalls = carrierConfig.getBoolean(
1713                 CarrierConfigManager.KEY_IGNORE_DATA_ENABLED_CHANGED_FOR_VIDEO_CALLS);
1714         mIsViLteDataMetered = carrierConfig.getBoolean(
1715                 CarrierConfigManager.KEY_VILTE_DATA_IS_METERED_BOOL);
1716         mSupportPauseVideo = carrierConfig.getBoolean(
1717                 CarrierConfigManager.KEY_SUPPORT_PAUSE_IMS_VIDEO_CALLS_BOOL);
1718         mAlwaysPlayRemoteHoldTone = carrierConfig.getBoolean(
1719                 CarrierConfigManager.KEY_ALWAYS_PLAY_REMOTE_HOLD_TONE_BOOL);
1720         mAutoRetryFailedWifiEmergencyCall = carrierConfig.getBoolean(
1721                 CarrierConfigManager.KEY_AUTO_RETRY_FAILED_WIFI_EMERGENCY_CALL);
1722         mSupportCepOnPeer = carrierConfig.getBoolean(
1723                 CarrierConfigManager.KEY_SUPPORT_IMS_CONFERENCE_EVENT_PACKAGE_ON_PEER_BOOL);
1724         mSupportD2DUsingRtp = carrierConfig.getBoolean(
1725                 CarrierConfigManager.KEY_SUPPORTS_DEVICE_TO_DEVICE_COMMUNICATION_USING_RTP_BOOL);
1726         mSupportSdpForRtpHeaderExtensions = carrierConfig.getBoolean(
1727                 CarrierConfigManager
1728                         .KEY_SUPPORTS_SDP_NEGOTIATION_OF_D2D_RTP_HEADER_EXTENSIONS_BOOL);
1729 
1730         if (mPhone.getContext().getResources().getBoolean(
1731                 com.android.internal.R.bool.config_allow_ussd_over_ims)) {
1732             mUssdMethod = carrierConfig.getInt(CarrierConfigManager.KEY_CARRIER_USSD_METHOD_INT);
1733         }
1734 
1735         if (!mImsReasonCodeMap.isEmpty()) {
1736             mImsReasonCodeMap.clear();
1737         }
1738         String[] mappings = carrierConfig
1739                 .getStringArray(CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY);
1740         if (mappings != null && mappings.length > 0) {
1741             for (String mapping : mappings) {
1742                 String[] values = mapping.split(Pattern.quote("|"));
1743                 if (values.length != 3) {
1744                     continue;
1745                 }
1746 
1747                 try {
1748                     Integer fromCode;
1749                     if (values[0].equals("*")) {
1750                         fromCode = null;
1751                     } else {
1752                         fromCode = Integer.parseInt(values[0]);
1753                     }
1754                     String message = values[1];
1755                     if (message == null) {
1756                         message = "";
1757                     }
1758                     else if (message.equals("*")) {
1759                         message = null;
1760                     }
1761                     int toCode = Integer.parseInt(values[2]);
1762 
1763                     addReasonCodeRemapping(fromCode, message, toCode);
1764                     log("Loaded ImsReasonInfo mapping :" +
1765                             " fromCode = " + (fromCode == null ? "any" : fromCode) +
1766                             " ; message = " + (message == null ? "any" : message) +
1767                             " ; toCode = " + toCode);
1768                 } catch (NumberFormatException nfe) {
1769                     loge("Invalid ImsReasonInfo mapping found: " + mapping);
1770                 }
1771             }
1772         } else {
1773             log("No carrier ImsReasonInfo mappings defined.");
1774         }
1775     }
1776 
1777     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
handleEcmTimer(int action)1778     private void handleEcmTimer(int action) {
1779         mPhone.handleTimerInEmergencyCallbackMode(action);
1780     }
1781 
dialInternal(ImsPhoneConnection conn, int clirMode, int videoState, Bundle intentExtras)1782     private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState,
1783             Bundle intentExtras) {
1784         dialInternal(conn, clirMode, videoState, ImsReasonInfo.CODE_UNSPECIFIED,
1785                 TelephonyManager.NETWORK_TYPE_UNKNOWN, intentExtras);
1786     }
1787 
dialInternal(ImsPhoneConnection conn, int clirMode, int videoState, int retryCallFailCause, int retryCallFailNetworkType, Bundle intentExtras)1788     private void dialInternal(ImsPhoneConnection conn, int clirMode, int videoState,
1789             int retryCallFailCause, int retryCallFailNetworkType, Bundle intentExtras) {
1790 
1791         if (conn == null) {
1792             return;
1793         }
1794 
1795         if (!conn.isAdhocConference() &&
1796                 (conn.getAddress()== null || conn.getAddress().length() == 0
1797                 || conn.getAddress().indexOf(PhoneNumberUtils.WILD) >= 0)) {
1798             // Phone number is invalid
1799             conn.setDisconnectCause(DisconnectCause.INVALID_NUMBER);
1800             sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
1801             return;
1802         }
1803 
1804         // Always unmute when initiating a new call
1805         setMute(false);
1806         boolean isEmergencyCall = conn.isEmergency();
1807         int serviceType = isEmergencyCall
1808                 ? ImsCallProfile.SERVICE_TYPE_EMERGENCY : ImsCallProfile.SERVICE_TYPE_NORMAL;
1809         int callType = ImsCallProfile.getCallTypeFromVideoState(videoState);
1810         //TODO(vt): Is this sufficient?  At what point do we know the video state of the call?
1811         conn.setVideoState(videoState);
1812 
1813         try {
1814             String[] callees = new String[] { conn.getAddress() };
1815             ImsCallProfile profile = mImsManager.createCallProfile(serviceType, callType);
1816             if (conn.isAdhocConference()) {
1817                 profile.setCallExtraBoolean(ImsCallProfile.EXTRA_CONFERENCE, true);
1818                 // Also set value for EXTRA_CONFERENCE_DEPRECATED in case receivers are using old
1819                 // values.
1820                 profile.setCallExtraBoolean(ImsCallProfile.EXTRA_CONFERENCE_DEPRECATED, true);
1821             }
1822             profile.setCallExtraInt(ImsCallProfile.EXTRA_OIR, clirMode);
1823             profile.setCallExtraInt(ImsCallProfile.EXTRA_RETRY_CALL_FAIL_REASON,
1824                     retryCallFailCause);
1825             profile.setCallExtraInt(ImsCallProfile.EXTRA_RETRY_CALL_FAIL_NETWORKTYPE,
1826                     retryCallFailNetworkType);
1827 
1828             if (isEmergencyCall) {
1829                 // Set emergency call information in ImsCallProfile
1830                 setEmergencyCallInfo(profile, conn);
1831             }
1832 
1833             // Translate call subject intent-extra from Telecom-specific extra key to the
1834             // ImsCallProfile key.
1835             if (intentExtras != null) {
1836                 if (intentExtras.containsKey(android.telecom.TelecomManager.EXTRA_CALL_SUBJECT)) {
1837                     intentExtras.putString(ImsCallProfile.EXTRA_DISPLAY_TEXT,
1838                             cleanseInstantLetteringMessage(intentExtras.getString(
1839                                     android.telecom.TelecomManager.EXTRA_CALL_SUBJECT))
1840                     );
1841                     profile.setCallExtra(ImsCallProfile.EXTRA_CALL_SUBJECT,
1842                             intentExtras.getString(TelecomManager.EXTRA_CALL_SUBJECT));
1843                 }
1844 
1845                 if (intentExtras.containsKey(android.telecom.TelecomManager.EXTRA_PRIORITY)) {
1846                     profile.setCallExtraInt(ImsCallProfile.EXTRA_PRIORITY, intentExtras.getInt(
1847                             android.telecom.TelecomManager.EXTRA_PRIORITY));
1848                 }
1849 
1850                 if (intentExtras.containsKey(android.telecom.TelecomManager.EXTRA_LOCATION)) {
1851                     profile.setCallExtraParcelable(ImsCallProfile.EXTRA_LOCATION,
1852                             intentExtras.getParcelable(
1853                                     android.telecom.TelecomManager.EXTRA_LOCATION));
1854                 }
1855 
1856                 if (intentExtras.containsKey(
1857                         android.telecom.TelecomManager.EXTRA_OUTGOING_PICTURE)) {
1858                     String url = TelephonyLocalConnection.getCallComposerServerUrlForHandle(
1859                             mPhone.getSubId(), ((ParcelUuid) intentExtras.getParcelable(
1860                                     TelecomManager.EXTRA_OUTGOING_PICTURE)).getUuid());
1861                     profile.setCallExtra(ImsCallProfile.EXTRA_PICTURE_URL, url);
1862                 }
1863 
1864                 if (conn.hasRttTextStream()) {
1865                     profile.mMediaProfile.mRttMode = ImsStreamMediaProfile.RTT_MODE_FULL;
1866                 }
1867 
1868                 if (intentExtras.containsKey(ImsCallProfile.EXTRA_IS_CALL_PULL)) {
1869                     profile.mCallExtras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL,
1870                             intentExtras.getBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL));
1871                     int dialogId = intentExtras.getInt(
1872                             ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID);
1873                     conn.setIsPulledCall(true);
1874                     conn.setPulledDialogId(dialogId);
1875                 }
1876 
1877                 // Pack the OEM-specific call extras.
1878                 profile.mCallExtras.putBundle(ImsCallProfile.EXTRA_OEM_EXTRAS, intentExtras);
1879 
1880                 // NOTE: Extras to be sent over the network are packed into the
1881                 // intentExtras individually, with uniquely defined keys.
1882                 // These key-value pairs are processed by IMS Service before
1883                 // being sent to the lower layers/to the network.
1884             }
1885 
1886             mPhone.getVoiceCallSessionStats().onImsDial(conn);
1887 
1888             ImsCall imsCall = mImsManager.makeCall(profile,
1889                     conn.isAdhocConference() ? conn.getParticipantsToDial() : callees,
1890                     mImsCallListener);
1891             conn.setImsCall(imsCall);
1892 
1893             mMetrics.writeOnImsCallStart(mPhone.getPhoneId(), imsCall.getSession());
1894 
1895             setVideoCallProvider(conn, imsCall);
1896             conn.setAllowAddCallDuringVideoCall(mAllowAddCallDuringVideoCall);
1897             conn.setAllowHoldingVideoCall(mAllowHoldingVideoCall);
1898         } catch (ImsException e) {
1899             loge("dialInternal : " + e);
1900             mOperationLocalLog.log("dialInternal exception: " + e);
1901             conn.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
1902             sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
1903         } catch (RemoteException e) {
1904         }
1905     }
1906 
1907     /**
1908      * Accepts a call with the specified video state.  The video state is the video state that the
1909      * user has agreed upon in the InCall UI.
1910      *
1911      * @param videoState The video State
1912      * @throws CallStateException
1913      */
acceptCall(int videoState)1914     public void acceptCall(int videoState) throws CallStateException {
1915         if (DBG) log("acceptCall");
1916         mOperationLocalLog.log("accepted incoming call");
1917 
1918         if (mForegroundCall.getState().isAlive()
1919                 && mBackgroundCall.getState().isAlive()) {
1920             throw new CallStateException("cannot accept call");
1921         }
1922 
1923         if ((mRingingCall.getState() == ImsPhoneCall.State.WAITING)
1924                 && mForegroundCall.getState().isAlive()) {
1925             setMute(false);
1926 
1927             boolean answeringWillDisconnect = false;
1928             ImsCall activeCall = mForegroundCall.getImsCall();
1929             ImsCall ringingCall = mRingingCall.getImsCall();
1930             if (mForegroundCall.hasConnections() && mRingingCall.hasConnections()) {
1931                 answeringWillDisconnect =
1932                         shouldDisconnectActiveCallOnAnswer(activeCall, ringingCall);
1933             }
1934 
1935             // Cache video state for pending MT call.
1936             mPendingCallVideoState = videoState;
1937 
1938             if (answeringWillDisconnect) {
1939                 // We need to disconnect the foreground call before answering the background call.
1940                 mForegroundCall.hangup();
1941                 mPhone.getVoiceCallSessionStats().onImsAcceptCall(mRingingCall.getConnections());
1942                 try {
1943                     ringingCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState));
1944                 } catch (ImsException e) {
1945                     throw new CallStateException("cannot accept call");
1946                 }
1947             } else {
1948                 holdActiveCallForWaitingCall();
1949             }
1950         } else if (mRingingCall.getState().isRinging()) {
1951             if (DBG) log("acceptCall: incoming...");
1952             // Always unmute when answering a new call
1953             setMute(false);
1954             try {
1955                 ImsCall imsCall = mRingingCall.getImsCall();
1956                 if (imsCall != null) {
1957                     mPhone.getVoiceCallSessionStats().onImsAcceptCall(
1958                             mRingingCall.getConnections());
1959                     imsCall.accept(ImsCallProfile.getCallTypeFromVideoState(videoState));
1960                     mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
1961                             ImsCommand.IMS_CMD_ACCEPT);
1962                 } else {
1963                     throw new CallStateException("no valid ims call");
1964                 }
1965             } catch (ImsException e) {
1966                 throw new CallStateException("cannot accept call");
1967             }
1968         } else {
1969             throw new CallStateException("phone not ringing");
1970         }
1971     }
1972 
rejectCall()1973     public void rejectCall () throws CallStateException {
1974         if (DBG) log("rejectCall");
1975         mOperationLocalLog.log("rejected incoming call");
1976 
1977         if (mRingingCall.getState().isRinging()) {
1978             hangup(mRingingCall);
1979         } else {
1980             throw new CallStateException("phone not ringing");
1981         }
1982     }
1983 
1984     /**
1985      * Set the emergency call information if it is an emergency call.
1986      */
setEmergencyCallInfo(ImsCallProfile profile, Connection conn)1987     private void setEmergencyCallInfo(ImsCallProfile profile, Connection conn) {
1988         EmergencyNumber num = conn.getEmergencyNumberInfo();
1989         if (num != null) {
1990             profile.setEmergencyCallInfo(num, conn.hasKnownUserIntentEmergency());
1991         }
1992     }
1993 
1994     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
switchAfterConferenceSuccess()1995     private void switchAfterConferenceSuccess() {
1996         if (DBG) log("switchAfterConferenceSuccess fg =" + mForegroundCall.getState() +
1997                 ", bg = " + mBackgroundCall.getState());
1998 
1999         if (mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING) {
2000             log("switchAfterConferenceSuccess");
2001             mForegroundCall.switchWith(mBackgroundCall);
2002         }
2003     }
2004 
holdActiveCallForPendingMo()2005     private void holdActiveCallForPendingMo() throws CallStateException {
2006         if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_HOLD
2007                 || mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) {
2008             logi("Ignoring hold request while already holding or swapping");
2009             return;
2010         }
2011         HoldSwapState oldHoldState = mHoldSwitchingState;
2012         ImsCall callToHold = mForegroundCall.getImsCall();
2013 
2014         mHoldSwitchingState = HoldSwapState.HOLDING_TO_DIAL_OUTGOING;
2015         logHoldSwapState("holdActiveCallForPendingMo");
2016 
2017         mForegroundCall.switchWith(mBackgroundCall);
2018         try {
2019             callToHold.hold();
2020             mMetrics.writeOnImsCommand(mPhone.getPhoneId(), callToHold.getSession(),
2021                     ImsCommand.IMS_CMD_HOLD);
2022         } catch (ImsException e) {
2023             mForegroundCall.switchWith(mBackgroundCall);
2024             mHoldSwitchingState = oldHoldState;
2025             logHoldSwapState("holdActiveCallForPendingMo - fail");
2026             throw new CallStateException(e.getMessage());
2027         }
2028     }
2029 
2030     /**
2031      * Holds the active call, possibly resuming the already-held background call if it exists.
2032      */
holdActiveCall()2033     public void holdActiveCall() throws CallStateException {
2034         if (mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE) {
2035             if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_HOLD
2036                     || mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) {
2037                 logi("Ignoring hold request while already holding or swapping");
2038                 return;
2039             }
2040             HoldSwapState oldHoldState = mHoldSwitchingState;
2041             ImsCall callToHold = mForegroundCall.getImsCall();
2042             if (mBackgroundCall.getState().isAlive()) {
2043                 mCallExpectedToResume = mBackgroundCall.getImsCall();
2044                 mHoldSwitchingState = HoldSwapState.SWAPPING_ACTIVE_AND_HELD;
2045             } else {
2046                 mHoldSwitchingState = HoldSwapState.PENDING_SINGLE_CALL_HOLD;
2047             }
2048             logHoldSwapState("holdActiveCall");
2049             mForegroundCall.switchWith(mBackgroundCall);
2050             try {
2051                 callToHold.hold();
2052                 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), callToHold.getSession(),
2053                         ImsCommand.IMS_CMD_HOLD);
2054             } catch (ImsException | NullPointerException e) {
2055                 mForegroundCall.switchWith(mBackgroundCall);
2056                 mHoldSwitchingState = oldHoldState;
2057                 logHoldSwapState("holdActiveCall - fail");
2058                 throw new CallStateException(e.getMessage());
2059             }
2060         }
2061     }
2062 
2063     /**
2064      * Hold the currently active call in order to answer the waiting call.
2065      */
holdActiveCallForWaitingCall()2066     public void holdActiveCallForWaitingCall() throws CallStateException {
2067         boolean switchingWithWaitingCall = !mBackgroundCall.getState().isAlive()
2068                 && mRingingCall.getState() == ImsPhoneCall.State.WAITING;
2069         if (switchingWithWaitingCall) {
2070             ImsCall callToHold = mForegroundCall.getImsCall();
2071             HoldSwapState oldHoldState = mHoldSwitchingState;
2072             mHoldSwitchingState = HoldSwapState.HOLDING_TO_ANSWER_INCOMING;
2073             ImsCall callExpectedToResume = mCallExpectedToResume;
2074             mCallExpectedToResume = mRingingCall.getImsCall();
2075             mForegroundCall.switchWith(mBackgroundCall);
2076             logHoldSwapState("holdActiveCallForWaitingCall");
2077             try {
2078                 callToHold.hold();
2079                 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), callToHold.getSession(),
2080                         ImsCommand.IMS_CMD_HOLD);
2081             } catch (ImsException e) {
2082                 mForegroundCall.switchWith(mBackgroundCall);
2083                 mHoldSwitchingState = oldHoldState;
2084                 mCallExpectedToResume = callExpectedToResume;
2085                 logHoldSwapState("holdActiveCallForWaitingCall - fail");
2086                 throw new CallStateException(e.getMessage());
2087             }
2088         }
2089     }
2090 
2091     /**
2092      * Unhold the currently held call.
2093      */
unholdHeldCall()2094     public void unholdHeldCall() throws CallStateException {
2095         ImsCall imsCall = mBackgroundCall.getImsCall();
2096         if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_UNHOLD
2097                 || mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) {
2098             logi("Ignoring unhold request while already unholding or swapping");
2099             return;
2100         }
2101         if (imsCall != null) {
2102             mCallExpectedToResume = imsCall;
2103             HoldSwapState oldHoldState = mHoldSwitchingState;
2104             mHoldSwitchingState = HoldSwapState.PENDING_SINGLE_CALL_UNHOLD;
2105             mForegroundCall.switchWith(mBackgroundCall);
2106             logHoldSwapState("unholdCurrentCall");
2107             try {
2108                 imsCall.resume();
2109                 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
2110                         ImsCommand.IMS_CMD_RESUME);
2111             } catch (ImsException e) {
2112                 mForegroundCall.switchWith(mBackgroundCall);
2113                 mHoldSwitchingState = oldHoldState;
2114                 logHoldSwapState("unholdCurrentCall - fail");
2115                 throw new CallStateException(e.getMessage());
2116             }
2117         }
2118     }
2119 
resumeForegroundCall()2120     private void resumeForegroundCall() throws ImsException {
2121         //resume foreground call after holding background call
2122         //they were switched before holding
2123         ImsCall imsCall = mForegroundCall.getImsCall();
2124         if (imsCall != null) {
2125             if (!imsCall.isPendingHold()) {
2126                 imsCall.resume();
2127                 mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
2128                         ImsCommand.IMS_CMD_RESUME);
2129             } else {
2130                 mHoldSwitchingState =
2131                         HoldSwapState.PENDING_RESUME_FOREGROUND_AFTER_HOLD;
2132                 logHoldSwapState("resumeForegroundCall - unhold pending; resume request again");
2133             }
2134         }
2135     }
2136 
answerWaitingCall()2137     private void answerWaitingCall() throws ImsException {
2138         //accept waiting call after holding background call
2139         ImsCall imsCall = mRingingCall.getImsCall();
2140         if (imsCall != null) {
2141             mPhone.getVoiceCallSessionStats().onImsAcceptCall(mRingingCall.getConnections());
2142             imsCall.accept(
2143                     ImsCallProfile.getCallTypeFromVideoState(mPendingCallVideoState));
2144             mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
2145                     ImsCommand.IMS_CMD_ACCEPT);
2146         }
2147     }
2148 
2149     // Clean up expired cache entries.
maintainConnectTimeCache()2150     private void maintainConnectTimeCache() {
2151         long threshold = SystemClock.elapsedRealtime() - TIMEOUT_PARTICIPANT_CONNECT_TIME_CACHE_MS;
2152         // The cached time is the system elapsed millisecond when the CacheEntry is created.
2153         mPhoneNumAndConnTime.entrySet().removeIf(e -> e.getValue().mCachedTime < threshold);
2154         // Remove all the cached records which are older than current caching threshold. Since the
2155         // queue is FIFO, keep polling records until the queue is empty or the head of the queue is
2156         // fresh enough.
2157         while (!mUnknownPeerConnTime.isEmpty()
2158                 && mUnknownPeerConnTime.peek().mCachedTime < threshold) {
2159             mUnknownPeerConnTime.poll();
2160         }
2161     }
2162 
cacheConnectionTimeWithPhoneNumber(@onNull ImsPhoneConnection connection)2163     private void cacheConnectionTimeWithPhoneNumber(@NonNull ImsPhoneConnection connection) {
2164         int callDirection =
2165                 connection.isIncoming() ? android.telecom.Call.Details.DIRECTION_INCOMING
2166                         : android.telecom.Call.Details.DIRECTION_OUTGOING;
2167         CacheEntry cachedConnectTime = new CacheEntry(SystemClock.elapsedRealtime(),
2168                 connection.getConnectTime(), connection.getConnectTimeReal(), callDirection);
2169         maintainConnectTimeCache();
2170         if (PhoneConstants.PRESENTATION_ALLOWED == connection.getNumberPresentation()) {
2171             // In case of merging calls with the same number, use the latest connect time. Since
2172             // that call might be dropped and re-connected. So if the connectTime is earlier than
2173             // the cache, skip.
2174             String phoneNumber = getFormattedPhoneNumber(connection.getAddress());
2175             if (mPhoneNumAndConnTime.containsKey(phoneNumber)
2176                     && connection.getConnectTime()
2177                         <= mPhoneNumAndConnTime.get(phoneNumber).mConnectTime) {
2178                 // Use the latest connect time.
2179                 return;
2180             }
2181             mPhoneNumAndConnTime.put(phoneNumber, cachedConnectTime);
2182         } else {
2183             mUnknownPeerConnTime.add(cachedConnectTime);
2184         }
2185     }
2186 
findConnectionTimeUsePhoneNumber( @onNull ConferenceParticipant participant)2187     private CacheEntry findConnectionTimeUsePhoneNumber(
2188             @NonNull ConferenceParticipant participant) {
2189         maintainConnectTimeCache();
2190         if (PhoneConstants.PRESENTATION_ALLOWED == participant.getParticipantPresentation()) {
2191             if (participant.getHandle() == null
2192                     || participant.getHandle().getSchemeSpecificPart() == null) {
2193                 return null;
2194             }
2195 
2196             String number = ConferenceParticipant.getParticipantAddress(participant.getHandle(),
2197                     getCountryIso()).getSchemeSpecificPart();
2198             if (TextUtils.isEmpty(number)) {
2199                 return null;
2200             }
2201             String formattedNumber = getFormattedPhoneNumber(number);
2202             return mPhoneNumAndConnTime.get(formattedNumber);
2203         } else {
2204             return mUnknownPeerConnTime.poll();
2205         }
2206     }
2207 
getFormattedPhoneNumber(String number)2208     private String getFormattedPhoneNumber(String number) {
2209         String countryIso = getCountryIso();
2210         if (countryIso == null) {
2211             return number;
2212         }
2213         String phoneNumber = PhoneNumberUtils.formatNumberToE164(number, countryIso);
2214         return phoneNumber == null ? number : phoneNumber;
2215     }
2216 
getCountryIso()2217     private String getCountryIso() {
2218         int subId = mPhone.getSubId();
2219         SubscriptionInfo info =
2220                 SubscriptionManager.from(mPhone.getContext()).getActiveSubscriptionInfo(subId);
2221         return info == null ? null : info.getCountryIso();
2222     }
2223 
2224     public void
conference()2225     conference() {
2226         ImsCall fgImsCall = mForegroundCall.getImsCall();
2227         if (fgImsCall == null) {
2228             log("conference no foreground ims call");
2229             return;
2230         }
2231 
2232         ImsCall bgImsCall = mBackgroundCall.getImsCall();
2233         if (bgImsCall == null) {
2234             log("conference no background ims call");
2235             return;
2236         }
2237 
2238         if (fgImsCall.isCallSessionMergePending()) {
2239             log("conference: skip; foreground call already in process of merging.");
2240             return;
2241         }
2242 
2243         if (bgImsCall.isCallSessionMergePending()) {
2244             log("conference: skip; background call already in process of merging.");
2245             return;
2246         }
2247 
2248         // Keep track of the connect time of the earliest call so that it can be set on the
2249         // {@code ImsConference} when it is created.
2250         long foregroundConnectTime = mForegroundCall.getEarliestConnectTime();
2251         long backgroundConnectTime = mBackgroundCall.getEarliestConnectTime();
2252         long conferenceConnectTime;
2253         if (foregroundConnectTime > 0 && backgroundConnectTime > 0) {
2254             conferenceConnectTime = Math.min(mForegroundCall.getEarliestConnectTime(),
2255                     mBackgroundCall.getEarliestConnectTime());
2256             log("conference - using connect time = " + conferenceConnectTime);
2257         } else if (foregroundConnectTime > 0) {
2258             log("conference - bg call connect time is 0; using fg = " + foregroundConnectTime);
2259             conferenceConnectTime = foregroundConnectTime;
2260         } else {
2261             log("conference - fg call connect time is 0; using bg = " + backgroundConnectTime);
2262             conferenceConnectTime = backgroundConnectTime;
2263         }
2264 
2265         String foregroundId = "";
2266         ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection();
2267         if (foregroundConnection != null) {
2268             foregroundConnection.setConferenceConnectTime(conferenceConnectTime);
2269             foregroundConnection.handleMergeStart();
2270             foregroundId = foregroundConnection.getTelecomCallId();
2271             cacheConnectionTimeWithPhoneNumber(foregroundConnection);
2272         }
2273         String backgroundId = "";
2274         ImsPhoneConnection backgroundConnection = findConnection(bgImsCall);
2275         if (backgroundConnection != null) {
2276             backgroundConnection.handleMergeStart();
2277             backgroundId = backgroundConnection.getTelecomCallId();
2278             cacheConnectionTimeWithPhoneNumber(backgroundConnection);
2279         }
2280         log("conference: fgCallId=" + foregroundId + ", bgCallId=" + backgroundId);
2281         mOperationLocalLog.log("conference: fgCallId=" + foregroundId + ", bgCallId="
2282                 + backgroundId);
2283 
2284         try {
2285             fgImsCall.merge(bgImsCall);
2286         } catch (ImsException e) {
2287             log("conference " + e.getMessage());
2288             handleConferenceFailed(foregroundConnection, backgroundConnection);
2289         }
2290     }
2291 
2292     /**
2293      * Connects the two calls and disconnects the subscriber from both calls. Throws a
2294      * {@link CallStateException} if there is an issue.
2295      * @throws CallStateException
2296      */
explicitCallTransfer()2297     public void explicitCallTransfer() throws CallStateException {
2298         ImsCall fgImsCall = mForegroundCall.getImsCall();
2299         ImsCall bgImsCall = mBackgroundCall.getImsCall();
2300         if ((fgImsCall == null) || (bgImsCall == null) || !canTransfer()) {
2301             throw new CallStateException("cannot transfer");
2302         }
2303 
2304         try {
2305             // Per 3GPP TS 24.629 - A.2, the signalling for a consultative transfer should send the
2306             // REFER on the background held call with the foreground call specified as the
2307             // destination.
2308             bgImsCall.consultativeTransfer(fgImsCall);
2309         } catch (ImsException e) {
2310             throw new CallStateException(e.getMessage());
2311         }
2312     }
2313 
2314     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2315     public void
clearDisconnected()2316     clearDisconnected() {
2317         if (DBG) log("clearDisconnected");
2318 
2319         internalClearDisconnected();
2320 
2321         updatePhoneState();
2322         mPhone.notifyPreciseCallStateChanged();
2323     }
2324 
2325     public boolean
canConference()2326     canConference() {
2327         return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE
2328             && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING
2329             && !mBackgroundCall.isFull()
2330             && !mForegroundCall.isFull();
2331     }
2332 
canAddVideoCallDuringImsAudioCall(int videoState)2333     private boolean canAddVideoCallDuringImsAudioCall(int videoState) {
2334         if (mAllowHoldingVideoCall) {
2335             return true;
2336         }
2337 
2338         ImsCall call = mForegroundCall.getImsCall();
2339         if (call == null) {
2340             call = mBackgroundCall.getImsCall();
2341         }
2342 
2343         boolean isImsAudioCallActiveOrHolding = (mForegroundCall.getState() == Call.State.ACTIVE ||
2344                mBackgroundCall.getState() == Call.State.HOLDING) && call != null &&
2345                !call.isVideoCall();
2346 
2347         /* return TRUE if there doesn't exist ims audio call in either active
2348            or hold states */
2349         return !isImsAudioCallActiveOrHolding || !VideoProfile.isVideo(videoState);
2350     }
2351 
2352 
2353     /**
2354      * Determines if there are issues which would preclude dialing an outgoing call.  Throws a
2355      * {@link CallStateException} if there is an issue.
2356      * @throws CallStateException
2357      */
checkForDialIssues()2358     public void checkForDialIssues() throws CallStateException {
2359         boolean disableCall = TelephonyProperties.disable_call().orElse(false);
2360         if (disableCall) {
2361             throw new CallStateException(CallStateException.ERROR_CALLING_DISABLED,
2362                     "ro.telephony.disable-call has been used to disable calling.");
2363         }
2364         if (mPendingMO != null) {
2365             throw new CallStateException(CallStateException.ERROR_ALREADY_DIALING,
2366                     "Another outgoing call is already being dialed.");
2367         }
2368         if (mRingingCall.isRinging()) {
2369             throw new CallStateException(CallStateException.ERROR_CALL_RINGING,
2370                     "Can't place a call while another is ringing.");
2371         }
2372         if (mForegroundCall.getState().isAlive() & mBackgroundCall.getState().isAlive()) {
2373             throw new CallStateException(CallStateException.ERROR_TOO_MANY_CALLS,
2374                     "Already an active foreground and background call.");
2375         }
2376     }
2377 
2378     public boolean
canTransfer()2379     canTransfer() {
2380         return mForegroundCall.getState() == ImsPhoneCall.State.ACTIVE
2381             && mBackgroundCall.getState() == ImsPhoneCall.State.HOLDING;
2382     }
2383 
2384     //***** Private Instance Methods
2385 
2386     private void
internalClearDisconnected()2387     internalClearDisconnected() {
2388         mRingingCall.clearDisconnected();
2389         mForegroundCall.clearDisconnected();
2390         mBackgroundCall.clearDisconnected();
2391         mHandoverCall.clearDisconnected();
2392     }
2393 
2394     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
2395     private void
updatePhoneState()2396     updatePhoneState() {
2397         PhoneConstants.State oldState = mState;
2398 
2399         boolean isPendingMOIdle = mPendingMO == null || !mPendingMO.getState().isAlive();
2400 
2401         if (mRingingCall.isRinging()) {
2402             mState = PhoneConstants.State.RINGING;
2403         } else if (!isPendingMOIdle || !mForegroundCall.isIdle() || !mBackgroundCall.isIdle()) {
2404             // There is a non-idle call, so we're off the hook.
2405             mState = PhoneConstants.State.OFFHOOK;
2406         } else {
2407             mState = PhoneConstants.State.IDLE;
2408         }
2409 
2410         if (mState == PhoneConstants.State.IDLE && oldState != mState) {
2411             mVoiceCallEndedRegistrants.notifyRegistrants(
2412                     new AsyncResult(null, null, null));
2413         } else if (oldState == PhoneConstants.State.IDLE && oldState != mState) {
2414             mVoiceCallStartedRegistrants.notifyRegistrants (
2415                     new AsyncResult(null, null, null));
2416         }
2417 
2418         if (DBG) {
2419             log("updatePhoneState pendingMo = " + (mPendingMO == null ? "null"
2420                     : mPendingMO.getState() + "(" + mPendingMO.getTelecomCallId() + "/objId:"
2421                             + System.identityHashCode(mPendingMO) + ")")
2422                     + ", rng= " + mRingingCall.getState() + "("
2423                     + mRingingCall.getConnectionSummary()
2424                     + "), fg= " + mForegroundCall.getState() + "("
2425                     + mForegroundCall.getConnectionSummary()
2426                     + "), bg= " + mBackgroundCall.getState()
2427                     + "(" + mBackgroundCall.getConnectionSummary() + ")");
2428             log("updatePhoneState oldState=" + oldState + ", newState=" + mState);
2429         }
2430 
2431         if (mState != oldState) {
2432             mPhone.notifyPhoneStateChanged();
2433             mMetrics.writePhoneState(mPhone.getPhoneId(), mState);
2434             notifyPhoneStateChanged(oldState, mState);
2435         }
2436     }
2437 
2438     private void
handleRadioNotAvailable()2439     handleRadioNotAvailable() {
2440         // handlePollCalls will clear out its
2441         // call list when it gets the CommandException
2442         // error result from this
2443         pollCallsWhenSafe();
2444     }
2445 
2446     private void
dumpState()2447     dumpState() {
2448         List l;
2449 
2450         log("Phone State:" + mState);
2451 
2452         log("Ringing call: " + mRingingCall.toString());
2453 
2454         l = mRingingCall.getConnections();
2455         for (int i = 0, s = l.size(); i < s; i++) {
2456             log(l.get(i).toString());
2457         }
2458 
2459         log("Foreground call: " + mForegroundCall.toString());
2460 
2461         l = mForegroundCall.getConnections();
2462         for (int i = 0, s = l.size(); i < s; i++) {
2463             log(l.get(i).toString());
2464         }
2465 
2466         log("Background call: " + mBackgroundCall.toString());
2467 
2468         l = mBackgroundCall.getConnections();
2469         for (int i = 0, s = l.size(); i < s; i++) {
2470             log(l.get(i).toString());
2471         }
2472 
2473     }
2474 
2475     //***** Called from ImsPhone
2476     /**
2477      * Set the TTY mode. This is the actual tty mode (varies depending on peripheral status)
2478      */
setTtyMode(int ttyMode)2479     public void setTtyMode(int ttyMode) {
2480         if (mImsManager == null) {
2481             Log.w(LOG_TAG, "ImsManager is null when setting TTY mode");
2482             return;
2483         }
2484 
2485         try {
2486             mImsManager.setTtyMode(ttyMode);
2487         } catch (ImsException e) {
2488             loge("setTtyMode : " + e);
2489         }
2490     }
2491 
2492     /**
2493      * Sets the UI TTY mode. This is the preferred TTY mode that the user sets in the call
2494      * settings screen.
2495      */
setUiTTYMode(int uiTtyMode, Message onComplete)2496     public void setUiTTYMode(int uiTtyMode, Message onComplete) {
2497         if (mImsManager == null) {
2498             mPhone.sendErrorResponse(onComplete, getImsManagerIsNullException());
2499             return;
2500         }
2501 
2502         try {
2503             mImsManager.setUiTTYMode(mPhone.getContext(), uiTtyMode, onComplete);
2504         } catch (ImsException e) {
2505             loge("setUITTYMode : " + e);
2506             mPhone.sendErrorResponse(onComplete, e);
2507         }
2508     }
2509 
setMute(boolean mute)2510     public void setMute(boolean mute) {
2511         mDesiredMute = mute;
2512         mForegroundCall.setMute(mute);
2513     }
2514 
getMute()2515     public boolean getMute() {
2516         return mDesiredMute;
2517     }
2518 
2519     /**
2520      * Sends a DTMF code. According to <a href="http://tools.ietf.org/html/rfc2833">RFC 2833</a>,
2521      * event 0 ~ 9 maps to decimal value 0 ~ 9, '*' to 10, '#' to 11, event 'A' ~ 'D' to 12 ~ 15,
2522      * and event flash to 16. Currently, event flash is not supported.
2523      *
2524      * @param c that represents the DTMF to send. '0' ~ '9', 'A' ~ 'D', '*', '#' are valid inputs.
2525      * @param result the result message to send when done. If non-null, the {@link Message} must
2526      *         contain a valid {@link android.os.Messenger} in the {@link Message#replyTo} field,
2527      *         since this can be used across IPC boundaries.
2528      */
sendDtmf(char c, Message result)2529     public void sendDtmf(char c, Message result) {
2530         if (DBG) log("sendDtmf");
2531 
2532         ImsCall imscall = mForegroundCall.getImsCall();
2533         if (imscall != null) {
2534             imscall.sendDtmf(c, result);
2535         }
2536     }
2537 
2538     public void
startDtmf(char c)2539     startDtmf(char c) {
2540         if (DBG) log("startDtmf");
2541 
2542         ImsCall imscall = mForegroundCall.getImsCall();
2543         if (imscall != null) {
2544             imscall.startDtmf(c);
2545         } else {
2546             loge("startDtmf : no foreground call");
2547         }
2548     }
2549 
2550     public void
stopDtmf()2551     stopDtmf() {
2552         if (DBG) log("stopDtmf");
2553 
2554         ImsCall imscall = mForegroundCall.getImsCall();
2555         if (imscall != null) {
2556             imscall.stopDtmf();
2557         } else {
2558             loge("stopDtmf : no foreground call");
2559         }
2560     }
2561 
2562     //***** Called from ImsPhoneConnection
2563 
hangup(ImsPhoneConnection conn)2564     public void hangup (ImsPhoneConnection conn) throws CallStateException {
2565         if (DBG) log("hangup connection");
2566 
2567         if (conn.getOwner() != this) {
2568             throw new CallStateException ("ImsPhoneConnection " + conn
2569                     + "does not belong to ImsPhoneCallTracker " + this);
2570         }
2571 
2572         hangup(conn.getCall());
2573     }
2574 
2575     //***** Called from ImsPhoneCall
2576 
hangup(ImsPhoneCall call)2577     public void hangup (ImsPhoneCall call) throws CallStateException {
2578         hangup(call, android.telecom.Call.REJECT_REASON_DECLINED);
2579     }
2580 
hangup(ImsPhoneCall call, @android.telecom.Call.RejectReason int rejectReason)2581     public void hangup (ImsPhoneCall call, @android.telecom.Call.RejectReason int rejectReason)
2582             throws CallStateException {
2583         if (DBG) log("hangup call - reason=" + rejectReason);
2584 
2585         if (call.getConnectionsCount() == 0) {
2586             throw new CallStateException("no connections");
2587         }
2588 
2589         ImsCall imsCall = call.getImsCall();
2590         ImsPhoneConnection conn = findConnection(imsCall);
2591         boolean rejectCall = false;
2592 
2593         String logResult = "(undefined)";
2594         if (call == mRingingCall) {
2595             logResult = "(ringing) hangup incoming";
2596             rejectCall = true;
2597         } else if (call == mForegroundCall) {
2598             if (call.isDialingOrAlerting()) {
2599                 logResult = "(foregnd) hangup dialing or alerting...";
2600             } else {
2601                 logResult = "(foregnd) hangup foreground";
2602                 //held call will be resumed by onCallTerminated
2603             }
2604         } else if (call == mBackgroundCall) {
2605             logResult = "(backgnd) hangup waiting or background";
2606         } else if (call == mHandoverCall) {
2607             logResult = "(handover) hangup handover (SRVCC) call";
2608         } else {
2609             mOperationLocalLog.log("hangup: ImsPhoneCall " + System.identityHashCode(conn)
2610                     + " does not belong to ImsPhoneCallTracker " + this);
2611             throw new CallStateException ("ImsPhoneCall " + call +
2612                     "does not belong to ImsPhoneCallTracker " + this);
2613         }
2614         if (Phone.DEBUG_PHONE) log(logResult);
2615         mOperationLocalLog.log("hangup: " + logResult + ", connId="
2616                 + System.identityHashCode(conn));
2617 
2618         call.onHangupLocal();
2619 
2620         try {
2621             if (imsCall != null) {
2622                 if (rejectCall) {
2623                     if (rejectReason == android.telecom.Call.REJECT_REASON_UNWANTED) {
2624                         imsCall.reject(ImsReasonInfo.CODE_SIP_USER_MARKED_UNWANTED);
2625                     } else {
2626                         imsCall.reject(ImsReasonInfo.CODE_USER_DECLINE);
2627                     }
2628                     mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
2629                             ImsCommand.IMS_CMD_REJECT);
2630                 } else {
2631                     imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
2632                     mMetrics.writeOnImsCommand(mPhone.getPhoneId(), imsCall.getSession(),
2633                             ImsCommand.IMS_CMD_TERMINATE);
2634                 }
2635             } else if (mPendingMO != null && call == mForegroundCall) {
2636                 // is holding a foreground call
2637                 mPendingMO.update(null, ImsPhoneCall.State.DISCONNECTED);
2638                 mPendingMO.onDisconnect();
2639                 removeConnection(mPendingMO);
2640                 mPendingMO = null;
2641                 updatePhoneState();
2642                 removeMessages(EVENT_DIAL_PENDINGMO);
2643             }
2644         } catch (ImsException e) {
2645             mOperationLocalLog.log("hangup: ImsException=" + e);
2646             throw new CallStateException(e.getMessage());
2647         }
2648 
2649         mPhone.notifyPreciseCallStateChanged();
2650     }
2651 
callEndCleanupHandOverCallIfAny()2652     void callEndCleanupHandOverCallIfAny() {
2653         List<Connection> connections = mHandoverCall.getConnections();
2654         if (connections.size() > 0) {
2655             if (DBG) log("callEndCleanupHandOverCallIfAny, mHandoverCall.mConnections="
2656                     + mHandoverCall.getConnections());
2657             mHandoverCall.clearConnections();
2658             mConnections.clear();
2659             mState = PhoneConstants.State.IDLE;
2660         }
2661     }
2662 
2663 
sendUSSD(String ussdString, Message response)2664     public void sendUSSD (String ussdString, Message response) {
2665         if (DBG) log("sendUSSD");
2666 
2667         try {
2668             if (mUssdSession != null) {
2669                 // Doesn't need mPendingUssd here. Listeners would use it if not null.
2670                 mPendingUssd = null;
2671                 mUssdSession.sendUssd(ussdString);
2672                 AsyncResult.forMessage(response, null, null);
2673                 response.sendToTarget();
2674                 return;
2675             }
2676 
2677             if (mImsManager == null) {
2678                 mPhone.sendErrorResponse(response, getImsManagerIsNullException());
2679                 return;
2680             }
2681 
2682             String[] callees = new String[] { ussdString };
2683             ImsCallProfile profile = mImsManager.createCallProfile(
2684                     ImsCallProfile.SERVICE_TYPE_NORMAL, ImsCallProfile.CALL_TYPE_VOICE);
2685             profile.setCallExtraInt(ImsCallProfile.EXTRA_DIALSTRING,
2686                     ImsCallProfile.DIALSTRING_USSD);
2687 
2688             mUssdSession = mImsManager.makeCall(profile, callees, mImsUssdListener);
2689             mPendingUssd = response;
2690             if (DBG) log("pending ussd updated, " + mPendingUssd);
2691         } catch (ImsException e) {
2692             loge("sendUSSD : " + e);
2693             mPhone.sendErrorResponse(response, e);
2694         }
2695     }
2696 
2697     /**
2698      * Cancel USSD session.
2699      *
2700      * @param msg The message to dispatch when the USSD session terminated.
2701      */
cancelUSSD(Message msg)2702     public void cancelUSSD(Message msg) {
2703         if (mUssdSession == null) return;
2704         mPendingUssd = msg;
2705         mUssdSession.terminate(ImsReasonInfo.CODE_USER_TERMINATED);
2706     }
2707 
2708     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
findConnection(final ImsCall imsCall)2709     private synchronized ImsPhoneConnection findConnection(final ImsCall imsCall) {
2710         for (ImsPhoneConnection conn : mConnections) {
2711             if (conn.getImsCall() == imsCall) {
2712                 return conn;
2713             }
2714         }
2715         return null;
2716     }
2717 
2718     /**
2719      * Given a connection, detach it from any {@link ImsPhoneCall} it is associated with, remove it
2720      * from the connections lists, and ensure if it was the pending MO connection it gets removed
2721      * from there as well.
2722      * @param conn The connection to cleanup and remove.
2723      */
cleanupAndRemoveConnection(ImsPhoneConnection conn)2724     public synchronized void cleanupAndRemoveConnection(ImsPhoneConnection conn) {
2725         mOperationLocalLog.log("cleanupAndRemoveConnection: " + conn);
2726         // If the connection is attached to a call, detach it.
2727         if (conn.getCall() != null) {
2728             conn.getCall().detach(conn);
2729         }
2730         // Remove it from the connection list.
2731         removeConnection(conn);
2732 
2733         // Finally, if it was the pending MO, then ensure that connection gets cleaned up as well.
2734         if (conn == mPendingMO) {
2735             mPendingMO.finalize();
2736             mPendingMO = null;
2737         }
2738     }
2739 
2740     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
removeConnection(ImsPhoneConnection conn)2741     public synchronized void removeConnection(ImsPhoneConnection conn) {
2742         mConnections.remove(conn);
2743 
2744         // If not emergency call is remaining, notify emergency call registrants
2745         if (mIsInEmergencyCall) {
2746             boolean isEmergencyCallInList = false;
2747             // if no emergency calls pending, set this to false
2748             for (ImsPhoneConnection imsPhoneConnection : mConnections) {
2749                 if (imsPhoneConnection != null && imsPhoneConnection.isEmergency() == true) {
2750                     isEmergencyCallInList = true;
2751                     break;
2752                 }
2753             }
2754 
2755             if (!isEmergencyCallInList) {
2756                 if (mPhone.isEcmCanceledForEmergency()) {
2757                     mPhone.handleTimerInEmergencyCallbackMode(ImsPhone.RESTART_ECM_TIMER);
2758                 }
2759                 mIsInEmergencyCall = false;
2760                 mPhone.sendEmergencyCallStateChange(false);
2761             }
2762         }
2763     }
2764 
2765     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
addConnection(ImsPhoneConnection conn)2766     private synchronized void addConnection(ImsPhoneConnection conn) {
2767         mConnections.add(conn);
2768         if (conn.isEmergency()) {
2769             mIsInEmergencyCall = true;
2770             mPhone.sendEmergencyCallStateChange(true);
2771         }
2772     }
2773 
2774     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause)2775     private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause) {
2776         if (DBG) log("processCallStateChange " + imsCall + " state=" + state + " cause=" + cause);
2777         // This method is called on onCallUpdate() where there is not necessarily a call state
2778         // change. In these situations, we'll ignore the state related updates and only process
2779         // the change in media capabilities (as expected).  The default is to not ignore state
2780         // changes so we do not change existing behavior.
2781         processCallStateChange(imsCall, state, cause, false /* do not ignore state update */);
2782     }
2783 
2784     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause, boolean ignoreState)2785     private void processCallStateChange(ImsCall imsCall, ImsPhoneCall.State state, int cause,
2786             boolean ignoreState) {
2787         if (DBG) {
2788             log("processCallStateChange state=" + state + " cause=" + cause
2789                     + " ignoreState=" + ignoreState);
2790         }
2791 
2792         if (imsCall == null) return;
2793 
2794         boolean changed = false;
2795         ImsPhoneConnection conn = findConnection(imsCall);
2796 
2797         if (conn == null) {
2798             // TODO : what should be done?
2799             return;
2800         }
2801 
2802         // processCallStateChange is triggered for onCallUpdated as well.
2803         // onCallUpdated should not modify the state of the call
2804         // It should modify only other capabilities of call through updateMediaCapabilities
2805         // State updates will be triggered through individual callbacks
2806         // i.e. onCallHeld, onCallResume, etc and conn.update will be responsible for the update
2807         conn.updateMediaCapabilities(imsCall);
2808         if (ignoreState) {
2809             conn.updateAddressDisplay(imsCall);
2810             conn.updateExtras(imsCall);
2811             // Some devices will change the audio direction between major call state changes, so we
2812             // need to check whether to start or stop ringback
2813             conn.maybeChangeRingbackState();
2814 
2815             maybeSetVideoCallProvider(conn, imsCall);
2816             return;
2817         }
2818 
2819         // Do not log operations that do not change the state
2820         mOperationLocalLog.log("processCallStateChange: state=" + state + " cause=" + cause
2821                 + " connId=" + System.identityHashCode(conn));
2822 
2823         changed = conn.update(imsCall, state);
2824         if (state == ImsPhoneCall.State.DISCONNECTED) {
2825             changed = conn.onDisconnect(cause) || changed;
2826             //detach the disconnected connections
2827             conn.getCall().detach(conn);
2828             removeConnection(conn);
2829 
2830             // If the call being disconnected was the pending MO call we should clear it.
2831             if (mPendingMO == conn) {
2832                 mPendingMO.finalize();
2833                 mPendingMO = null;
2834             }
2835 
2836             // remove conference participants from the cached list when call is disconnected
2837             List<ConferenceParticipant> cpList = imsCall.getConferenceParticipants();
2838             if (cpList != null) {
2839                 for (ConferenceParticipant cp : cpList) {
2840                     String number = ConferenceParticipant.getParticipantAddress(cp.getHandle(),
2841                             getCountryIso()).getSchemeSpecificPart();
2842                     if (!TextUtils.isEmpty(number)) {
2843                         String formattedNumber = getFormattedPhoneNumber(number);
2844                         mPhoneNumAndConnTime.remove(formattedNumber);
2845                     }
2846                 }
2847             }
2848         } else {
2849             mPhone.getVoiceCallSessionStats().onCallStateChanged(conn.getCall());
2850         }
2851 
2852         if (changed) {
2853             if (conn.getCall() == mHandoverCall) return;
2854             updatePhoneState();
2855             mPhone.notifyPreciseCallStateChanged();
2856         }
2857     }
2858 
maybeSetVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)2859     private void maybeSetVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall) {
2860         android.telecom.Connection.VideoProvider connVideoProvider = conn.getVideoProvider();
2861         ImsCallSession callSession = imsCall.getCallSession();
2862         if (connVideoProvider != null || callSession == null
2863             || callSession.getVideoCallProvider() == null) {
2864             return;
2865         }
2866 
2867         try {
2868             setVideoCallProvider(conn, imsCall);
2869         } catch (RemoteException e) {
2870             loge("maybeSetVideoCallProvider: exception " + e);
2871         }
2872     }
2873 
2874     /**
2875      * Adds a reason code remapping, for test purposes.
2876      *
2877      * @param fromCode The from code, or {@code null} if all.
2878      * @param message The message to map.
2879      * @param toCode The code to remap to.
2880      */
2881     @VisibleForTesting
addReasonCodeRemapping(Integer fromCode, String message, Integer toCode)2882     public void addReasonCodeRemapping(Integer fromCode, String message, Integer toCode) {
2883         if (message != null) {
2884             message = message.toLowerCase();
2885         }
2886         mImsReasonCodeMap.put(new ImsReasonInfoKeyPair(fromCode, message), toCode);
2887     }
2888 
2889     /**
2890      * Returns the {@link ImsReasonInfo#getCode()}, potentially remapping to a new value based on
2891      * the {@link ImsReasonInfo#getCode()} and {@link ImsReasonInfo#getExtraMessage()}.
2892      *
2893      * See {@link #mImsReasonCodeMap}.
2894      *
2895      * @param reasonInfo The {@link ImsReasonInfo}.
2896      * @return The remapped code.
2897      */
2898     @VisibleForTesting
maybeRemapReasonCode(ImsReasonInfo reasonInfo)2899     public @ImsReasonInfo.ImsCode int maybeRemapReasonCode(ImsReasonInfo reasonInfo) {
2900         int code = reasonInfo.getCode();
2901         String reason = reasonInfo.getExtraMessage();
2902         if (reason == null) {
2903             reason = "";
2904         } else {
2905             reason = reason.toLowerCase();
2906         }
2907         log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = "
2908                 + reason);
2909         ImsReasonInfoKeyPair toCheck = new ImsReasonInfoKeyPair(code, reason);
2910         ImsReasonInfoKeyPair wildcardToCheck = new ImsReasonInfoKeyPair(null, reason);
2911         ImsReasonInfoKeyPair wildcardMessageToCheck = new ImsReasonInfoKeyPair(code, null);
2912 
2913         if (mImsReasonCodeMap.containsKey(toCheck)) {
2914             int toCode = mImsReasonCodeMap.get(toCheck);
2915 
2916             log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() + " ; message = "
2917                     + reason + " ; toCode = " + toCode);
2918             return toCode;
2919         } else if (!reason.isEmpty() && mImsReasonCodeMap.containsKey(wildcardToCheck)) {
2920             // Handle the case where a wildcard is specified for the fromCode; in this case we will
2921             // match without caring about the fromCode.
2922             // If the reason is empty, we won't do wildcard remapping; otherwise we'd basically be
2923             // able to remap all ImsReasonInfo codes to a single code, which is not desirable.
2924             int toCode = mImsReasonCodeMap.get(wildcardToCheck);
2925 
2926             log("maybeRemapReasonCode : fromCode(wildcard) = " + reasonInfo.getCode() +
2927                     " ; message = " + reason + " ; toCode = " + toCode);
2928             return toCode;
2929         }
2930         else if (mImsReasonCodeMap.containsKey(wildcardMessageToCheck)) {
2931             // Handle the case where a wildcard is specified for the reason.
2932             // For example, we can set these two strings in
2933             // CarrierConfigManager.KEY_IMS_REASONINFO_MAPPING_STRING_ARRAY:
2934             //   - "1014|call completed elsewhere|1014"
2935             //   - "1014|*|510"
2936             // to remap CODE_ANSWERED_ELSEWHERE to CODE_USER_TERMINATED_BY_REMOTE
2937             // when reason is NOT "call completed elsewhere".
2938             int toCode = mImsReasonCodeMap.get(wildcardMessageToCheck);
2939             log("maybeRemapReasonCode : fromCode = " + reasonInfo.getCode() +
2940                     " ; message(wildcard) = " + reason + " ; toCode = " + toCode);
2941             return toCode;
2942         }
2943         return code;
2944     }
2945 
2946     /**
2947      * Maps an {@link ImsReasonInfo} reason code to a {@link DisconnectCause} cause code.
2948      * The {@link Call.State} provided is the state of the call prior to disconnection.
2949      * @param reasonInfo the {@link ImsReasonInfo} for the disconnection.
2950      * @param callState The {@link Call.State} prior to disconnection.
2951      * @return The {@link DisconnectCause} code.
2952      */
2953     @VisibleForTesting
getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo, Call.State callState)2954     public int getDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo, Call.State callState) {
2955         int cause = DisconnectCause.ERROR_UNSPECIFIED;
2956 
2957         int code = maybeRemapReasonCode(reasonInfo);
2958         switch (code) {
2959             case ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL:
2960                 return DisconnectCause.IMS_SIP_ALTERNATE_EMERGENCY_CALL;
2961             case ImsReasonInfo.CODE_SIP_BAD_ADDRESS:
2962             case ImsReasonInfo.CODE_SIP_NOT_REACHABLE:
2963                 return DisconnectCause.NUMBER_UNREACHABLE;
2964 
2965             case ImsReasonInfo.CODE_SIP_BUSY:
2966                 return DisconnectCause.BUSY;
2967 
2968             case ImsReasonInfo.CODE_USER_TERMINATED:
2969                 return DisconnectCause.LOCAL;
2970 
2971             case ImsReasonInfo.CODE_LOCAL_ENDED_BY_CONFERENCE_MERGE:
2972                 return DisconnectCause.IMS_MERGED_SUCCESSFULLY;
2973 
2974             case ImsReasonInfo.CODE_LOCAL_CALL_DECLINE:
2975             case ImsReasonInfo.CODE_REMOTE_CALL_DECLINE:
2976             case ImsReasonInfo.CODE_REJECTED_ELSEWHERE:
2977                 // If the call has been declined locally (on this device), or on remotely (on
2978                 // another device using multiendpoint functionality), mark it as rejected.
2979                 return DisconnectCause.INCOMING_REJECTED;
2980 
2981             case ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE:
2982             case ImsReasonInfo.CODE_SIP_USER_REJECTED:
2983                 return DisconnectCause.NORMAL;
2984 
2985             case ImsReasonInfo.CODE_SIP_FORBIDDEN:
2986                 return DisconnectCause.SERVER_ERROR;
2987 
2988             case ImsReasonInfo.CODE_SIP_REDIRECTED:
2989             case ImsReasonInfo.CODE_SIP_NOT_ACCEPTABLE:
2990             case ImsReasonInfo.CODE_SIP_GLOBAL_ERROR:
2991                 return DisconnectCause.SERVER_ERROR;
2992 
2993             case ImsReasonInfo.CODE_EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE:
2994                 return DisconnectCause.EMERGENCY_CALL_OVER_WFC_NOT_AVAILABLE;
2995 
2996             case ImsReasonInfo.CODE_WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION:
2997                 return DisconnectCause.WFC_SERVICE_NOT_AVAILABLE_IN_THIS_LOCATION;
2998 
2999             case ImsReasonInfo.CODE_SIP_SERVICE_UNAVAILABLE:
3000             case ImsReasonInfo.CODE_SIP_SERVER_ERROR:
3001                 return DisconnectCause.SERVER_UNREACHABLE;
3002 
3003             case ImsReasonInfo.CODE_SIP_NOT_FOUND:
3004                 return DisconnectCause.INVALID_NUMBER;
3005 
3006             case ImsReasonInfo.CODE_LOCAL_NETWORK_ROAMING:
3007             case ImsReasonInfo.CODE_LOCAL_NETWORK_IP_CHANGED:
3008             case ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN:
3009             case ImsReasonInfo.CODE_LOCAL_SERVICE_UNAVAILABLE:
3010             case ImsReasonInfo.CODE_LOCAL_NOT_REGISTERED:
3011             case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_LTE_COVERAGE:
3012             case ImsReasonInfo.CODE_LOCAL_NETWORK_NO_SERVICE:
3013             case ImsReasonInfo.CODE_LOCAL_CALL_VCC_ON_PROGRESSING:
3014                 return DisconnectCause.OUT_OF_SERVICE;
3015 
3016             case ImsReasonInfo.CODE_SIP_REQUEST_TIMEOUT:
3017             case ImsReasonInfo.CODE_TIMEOUT_1XX_WAITING:
3018             case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER:
3019             case ImsReasonInfo.CODE_TIMEOUT_NO_ANSWER_CALL_UPDATE:
3020                 return DisconnectCause.TIMED_OUT;
3021 
3022             case ImsReasonInfo.CODE_LOCAL_POWER_OFF:
3023             case ImsReasonInfo.CODE_RADIO_OFF:
3024                 return DisconnectCause.POWER_OFF;
3025 
3026             case ImsReasonInfo.CODE_LOCAL_LOW_BATTERY:
3027             case ImsReasonInfo.CODE_LOW_BATTERY: {
3028                 if (callState == Call.State.DIALING) {
3029                     return DisconnectCause.DIAL_LOW_BATTERY;
3030                 } else {
3031                     return DisconnectCause.LOW_BATTERY;
3032                 }
3033             }
3034 
3035             case ImsReasonInfo.CODE_CALL_BARRED:
3036                 return DisconnectCause.CALL_BARRED;
3037 
3038             case ImsReasonInfo.CODE_FDN_BLOCKED:
3039                 return DisconnectCause.FDN_BLOCKED;
3040 
3041             case ImsReasonInfo.CODE_IMEI_NOT_ACCEPTED:
3042                 return DisconnectCause.IMEI_NOT_ACCEPTED;
3043 
3044             case ImsReasonInfo.CODE_ANSWERED_ELSEWHERE:
3045                 return DisconnectCause.ANSWERED_ELSEWHERE;
3046 
3047             case ImsReasonInfo.CODE_CALL_END_CAUSE_CALL_PULL:
3048                 return DisconnectCause.CALL_PULLED;
3049 
3050             case ImsReasonInfo.CODE_MAXIMUM_NUMBER_OF_CALLS_REACHED:
3051                 return DisconnectCause.MAXIMUM_NUMBER_OF_CALLS_REACHED;
3052 
3053             case ImsReasonInfo.CODE_DATA_DISABLED:
3054                 return DisconnectCause.DATA_DISABLED;
3055 
3056             case ImsReasonInfo.CODE_DATA_LIMIT_REACHED:
3057                 return DisconnectCause.DATA_LIMIT_REACHED;
3058 
3059             case ImsReasonInfo.CODE_WIFI_LOST:
3060                 return DisconnectCause.WIFI_LOST;
3061 
3062             case ImsReasonInfo.CODE_ACCESS_CLASS_BLOCKED:
3063                 return DisconnectCause.IMS_ACCESS_BLOCKED;
3064 
3065             case ImsReasonInfo.CODE_EMERGENCY_TEMP_FAILURE:
3066                 return DisconnectCause.EMERGENCY_TEMP_FAILURE;
3067 
3068             case ImsReasonInfo.CODE_EMERGENCY_PERM_FAILURE:
3069                 return DisconnectCause.EMERGENCY_PERM_FAILURE;
3070 
3071             case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_USSD:
3072                 return DisconnectCause.DIAL_MODIFIED_TO_USSD;
3073 
3074             case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_SS:
3075                 return DisconnectCause.DIAL_MODIFIED_TO_SS;
3076 
3077             case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL:
3078                 return DisconnectCause.DIAL_MODIFIED_TO_DIAL;
3079 
3080             case ImsReasonInfo.CODE_DIAL_MODIFIED_TO_DIAL_VIDEO:
3081                 return DisconnectCause.DIAL_MODIFIED_TO_DIAL_VIDEO;
3082 
3083             case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL:
3084                 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL;
3085 
3086             case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO:
3087                 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_DIAL_VIDEO;
3088 
3089             case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_SS:
3090                 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_SS;
3091 
3092             case ImsReasonInfo.CODE_DIAL_VIDEO_MODIFIED_TO_USSD:
3093                 return DisconnectCause.DIAL_VIDEO_MODIFIED_TO_USSD;
3094 
3095             case ImsReasonInfo.CODE_UNOBTAINABLE_NUMBER:
3096                 return DisconnectCause.UNOBTAINABLE_NUMBER;
3097 
3098             case ImsReasonInfo.CODE_MEDIA_NO_DATA:
3099                 return DisconnectCause.MEDIA_TIMEOUT;
3100 
3101             case ImsReasonInfo.CODE_UNSPECIFIED:
3102                 if (mPhone.getDefaultPhone().getServiceStateTracker().mRestrictedState
3103                         .isCsRestricted()) {
3104                     return DisconnectCause.CS_RESTRICTED;
3105                 } else if (mPhone.getDefaultPhone().getServiceStateTracker().mRestrictedState
3106                         .isCsEmergencyRestricted()) {
3107                     return DisconnectCause.CS_RESTRICTED_EMERGENCY;
3108                 } else if (mPhone.getDefaultPhone().getServiceStateTracker().mRestrictedState
3109                         .isCsNormalRestricted()) {
3110                     return DisconnectCause.CS_RESTRICTED_NORMAL;
3111                 }
3112                 break;
3113 
3114             case ImsReasonInfo.CODE_SIP_BAD_REQUEST:
3115             case ImsReasonInfo.CODE_REJECT_CALL_ON_OTHER_SUB:
3116             case ImsReasonInfo.CODE_REJECT_ONGOING_E911_CALL:
3117             case ImsReasonInfo.CODE_REJECT_ONGOING_CALL_SETUP:
3118             case ImsReasonInfo.CODE_REJECT_MAX_CALL_LIMIT_REACHED:
3119             case ImsReasonInfo.CODE_REJECT_ONGOING_CALL_TRANSFER:
3120             case ImsReasonInfo.CODE_REJECT_ONGOING_CONFERENCE_CALL:
3121             case ImsReasonInfo.CODE_REJECT_ONGOING_HANDOVER:
3122             case ImsReasonInfo.CODE_REJECT_ONGOING_CALL_UPGRADE:
3123                 return DisconnectCause.INCOMING_AUTO_REJECTED;
3124 
3125             default:
3126         }
3127 
3128         return cause;
3129     }
3130 
getPreciseDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo)3131     private int getPreciseDisconnectCauseFromReasonInfo(ImsReasonInfo reasonInfo) {
3132         return PRECISE_CAUSE_MAP.get(maybeRemapReasonCode(reasonInfo),
3133                 CallFailCause.ERROR_UNSPECIFIED);
3134     }
3135 
3136     /**
3137      * @return true if the phone is in Emergency Callback mode, otherwise false
3138      */
isPhoneInEcbMode()3139     private boolean isPhoneInEcbMode() {
3140         return mPhone != null && mPhone.isInEcm();
3141     }
3142 
3143     /**
3144      * Before dialing pending MO request, check for the Emergency Callback mode.
3145      * If device is in Emergency callback mode, then exit the mode before dialing pending MO.
3146      */
3147     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
dialPendingMO()3148     private void dialPendingMO() {
3149         boolean isPhoneInEcmMode = isPhoneInEcbMode();
3150         boolean isEmergencyNumber = mPendingMO.isEmergency();
3151         if ((!isPhoneInEcmMode) || (isPhoneInEcmMode && isEmergencyNumber)) {
3152             sendEmptyMessage(EVENT_DIAL_PENDINGMO);
3153         } else {
3154             sendEmptyMessage(EVENT_EXIT_ECBM_BEFORE_PENDINGMO);
3155         }
3156     }
3157 
3158     /**
3159      * Listen to the IMS call state change
3160      */
3161     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
3162     private ImsCall.Listener mImsCallListener = new ImsCall.Listener() {
3163         @Override
3164         public void onCallInitiating(ImsCall imsCall) {
3165             if (DBG) log("onCallInitiating");
3166             mPendingMO = null;
3167             processCallStateChange(imsCall, ImsPhoneCall.State.DIALING,
3168                     DisconnectCause.NOT_DISCONNECTED, true);
3169             mMetrics.writeOnImsCallInitiating(mPhone.getPhoneId(), imsCall.getCallSession());
3170         }
3171 
3172         @Override
3173         public void onCallProgressing(ImsCall imsCall) {
3174             if (DBG) log("onCallProgressing");
3175 
3176             mPendingMO = null;
3177             processCallStateChange(imsCall, ImsPhoneCall.State.ALERTING,
3178                     DisconnectCause.NOT_DISCONNECTED);
3179             mMetrics.writeOnImsCallProgressing(mPhone.getPhoneId(), imsCall.getCallSession());
3180         }
3181 
3182         @Override
3183         public void onCallStarted(ImsCall imsCall) {
3184             if (DBG) log("onCallStarted");
3185 
3186             if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) {
3187                 // If we put a call on hold to answer an incoming call, we should reset the
3188                 // variables that keep track of the switch here.
3189                 if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) {
3190                     if (DBG) log("onCallStarted: starting a call as a result of a switch.");
3191                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3192                     mCallExpectedToResume = null;
3193                     logHoldSwapState("onCallStarted");
3194                 }
3195             }
3196 
3197             mPendingMO = null;
3198             processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
3199                     DisconnectCause.NOT_DISCONNECTED);
3200 
3201             if (mNotifyVtHandoverToWifiFail && imsCall.isVideoCall() && !imsCall.isWifiCall()) {
3202                 if (isWifiConnected()) {
3203                     // Schedule check to see if handover succeeded.
3204                     sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, imsCall),
3205                             HANDOVER_TO_WIFI_TIMEOUT_MS);
3206                     mHasAttemptedStartOfCallHandover = false;
3207                 } else {
3208                     // No wifi connectivity, so keep track of network availability for potential
3209                     // handover.
3210                     registerForConnectivityChanges();
3211                     // No WIFI, so assume we've already attempted a handover.
3212                     mHasAttemptedStartOfCallHandover = true;
3213                 }
3214             }
3215             mMetrics.writeOnImsCallStarted(mPhone.getPhoneId(), imsCall.getCallSession());
3216         }
3217 
3218         @Override
3219         public void onCallUpdated(ImsCall imsCall) {
3220             if (DBG) log("onCallUpdated");
3221             if (imsCall == null) {
3222                 return;
3223             }
3224             ImsPhoneConnection conn = findConnection(imsCall);
3225             if (conn != null) {
3226                 if (DBG) log("onCallUpdated: profile is " + imsCall.getCallProfile());
3227                 processCallStateChange(imsCall, conn.getCall().mState,
3228                         DisconnectCause.NOT_DISCONNECTED, true /*ignore state update*/);
3229                 mMetrics.writeImsCallState(mPhone.getPhoneId(),
3230                         imsCall.getCallSession(), conn.getCall().mState);
3231             }
3232         }
3233 
3234         /**
3235          * onCallStartFailed will be invoked when:
3236          * case 1) Dialing fails
3237          * case 2) Ringing call is disconnected by local or remote user
3238          */
3239         @Override
3240         public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
3241             if (DBG) log("onCallStartFailed reasonCode=" + reasonInfo.getCode());
3242 
3243             int eccCategory = EmergencyNumber.EMERGENCY_SERVICE_CATEGORY_UNSPECIFIED;
3244             if (imsCall != null && imsCall.getCallProfile() != null) {
3245                 eccCategory = imsCall.getCallProfile().getEmergencyServiceCategories();
3246             }
3247 
3248             if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) {
3249                 // If we put a call on hold to answer an incoming call, we should reset the
3250                 // variables that keep track of the switch here.
3251                 if (mCallExpectedToResume != null && mCallExpectedToResume == imsCall) {
3252                     if (DBG) log("onCallStarted: starting a call as a result of a switch.");
3253                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3254                     mCallExpectedToResume = null;
3255                     logHoldSwapState("onCallStartFailed");
3256                 }
3257             }
3258 
3259             mPhone.getVoiceCallSessionStats()
3260                     .onImsCallStartFailed(
3261                             findConnection(imsCall),
3262                             new ImsReasonInfo(
3263                                     maybeRemapReasonCode(reasonInfo),
3264                                     reasonInfo.mExtraCode,
3265                                     reasonInfo.mExtraMessage));
3266 
3267             if (mPendingMO != null) {
3268                 // To initiate dialing circuit-switched call
3269                 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
3270                         && mRingingCall.getState() == ImsPhoneCall.State.IDLE
3271                         && isForegroundHigherPriority()) {
3272                     mForegroundCall.detach(mPendingMO);
3273                     removeConnection(mPendingMO);
3274                     mPendingMO.finalize();
3275                     mPendingMO = null;
3276                     // if we need to perform CSFB of call, hang up any background call
3277                     // before redialing if it is a lower priority.
3278                     if (mBackgroundCall.getState().isAlive()) {
3279                         try {
3280                             hangup(mBackgroundCall);
3281                             mPendingSilentRedialInfo = new Pair<>(reasonInfo.getExtraCode() ==
3282                                 ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY, eccCategory);
3283                         } catch (CallStateException ex) {
3284                             mPendingSilentRedialInfo = null;
3285                         }
3286                     } else {
3287                         updatePhoneState();
3288                         mPhone.initiateSilentRedial(reasonInfo.getExtraCode() ==
3289                                 ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY, eccCategory);
3290                     }
3291                     return;
3292                 } else {
3293                     sendCallStartFailedDisconnect(imsCall, reasonInfo);
3294                 }
3295                 mMetrics.writeOnImsCallStartFailed(mPhone.getPhoneId(), imsCall.getCallSession(),
3296                         reasonInfo);
3297             } else if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
3298                     && mForegroundCall.getState() == ImsPhoneCall.State.ALERTING) {
3299                 if (DBG) log("onCallStartFailed: Initiated call by silent redial"
3300                         + " under ALERTING state.");
3301                 ImsPhoneConnection conn = findConnection(imsCall);
3302                 if (conn != null) {
3303                     mForegroundCall.detach(conn);
3304                     removeConnection(conn);
3305                 }
3306                 updatePhoneState();
3307                 mPhone.initiateSilentRedial(reasonInfo.getExtraCode() ==
3308                         ImsReasonInfo.EXTRA_CODE_CALL_RETRY_EMERGENCY, eccCategory);
3309             }
3310         }
3311 
3312         @Override
3313         public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
3314             if (DBG) log("onCallTerminated reasonCode=" + reasonInfo.getCode());
3315 
3316             ImsPhoneConnection conn = findConnection(imsCall);
3317             Call.State callState;
3318             if (conn != null) {
3319                 callState = conn.getState();
3320             } else {
3321                 // Connection shouldn't be null, but if it is, we can assume the call was active.
3322                 // This call state is only used for determining which disconnect message to show in
3323                 // the case of the device's battery being low resulting in a call drop.
3324                 callState = Call.State.ACTIVE;
3325             }
3326             int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState);
3327 
3328             if (DBG) log("cause = " + cause + " conn = " + conn);
3329 
3330             if (conn != null) {
3331                 android.telecom.Connection.VideoProvider videoProvider = conn.getVideoProvider();
3332                 if (videoProvider instanceof ImsVideoCallProviderWrapper) {
3333                     ImsVideoCallProviderWrapper wrapper = (ImsVideoCallProviderWrapper)
3334                             videoProvider;
3335                     wrapper.unregisterForDataUsageUpdate(ImsPhoneCallTracker.this);
3336                     wrapper.removeImsVideoProviderCallback(conn);
3337                 }
3338             }
3339             if (mOnHoldToneId == System.identityHashCode(conn)) {
3340                 if (conn != null && mOnHoldToneStarted) {
3341                     mPhone.stopOnHoldTone(conn);
3342                 }
3343                 mOnHoldToneStarted = false;
3344                 mOnHoldToneId = -1;
3345             }
3346             if (conn != null) {
3347                 if (conn.isPulledCall() && (
3348                         reasonInfo.getCode() == ImsReasonInfo.CODE_CALL_PULL_OUT_OF_SYNC ||
3349                         reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_TEMPRARILY_UNAVAILABLE ||
3350                         reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_FORBIDDEN) &&
3351                         mPhone != null && mPhone.getExternalCallTracker() != null) {
3352 
3353                     log("Call pull failed.");
3354                     // Call was being pulled, but the call pull has failed -- inform the associated
3355                     // TelephonyConnection that the pull failed, and provide it with the original
3356                     // external connection which was pulled so that it can be swapped back.
3357                     conn.onCallPullFailed(mPhone.getExternalCallTracker()
3358                             .getConnectionById(conn.getPulledDialogId()));
3359                     // Do not mark as disconnected; the call will just change from being a regular
3360                     // call to being an external call again.
3361                     cause = DisconnectCause.NOT_DISCONNECTED;
3362 
3363                 } else if (conn.isIncoming() && conn.getConnectTime() == 0
3364                         && cause != DisconnectCause.ANSWERED_ELSEWHERE) {
3365                     // Two cases where the call is declared as rejected.
3366                     // 1. The disconnect was initiated by the user.  I.e. the connection's
3367                     // disconnect cause is LOCAL at this point.
3368                     // 2. The network provided disconnect cause is INCOMING_REJECTED.  This will be
3369                     // the case for ImsReasonInfo.CODE_USER_TERMINATED_BY_REMOTE and
3370                     // ImsReasonInfo.CODE_REJECTED_ELSEWHERE.
3371                     if (conn.getDisconnectCause() == DisconnectCause.LOCAL
3372                             || cause == DisconnectCause.INCOMING_REJECTED) {
3373                         // If the user initiated a disconnect of this connection, then we will treat
3374                         // this is a rejected call.
3375                         // Note; we record the fact that this is a local disconnect in
3376                         // ImsPhoneConnection#onHangupLocal
3377                         // Alternatively, the network can specify INCOMING_REJECTED as a result of
3378                         // remote reject on another device; we'll still treat as rejected.
3379                         cause = DisconnectCause.INCOMING_REJECTED;
3380                     } else {
3381                         // Otherwise in all other cases consider it missed.
3382                         cause = DisconnectCause.INCOMING_MISSED;
3383                     }
3384                     if (DBG) log("Incoming connection of 0 connect time detected - translated " +
3385                             "cause = " + cause);
3386                 }
3387             }
3388 
3389             if (cause == DisconnectCause.NORMAL && conn != null && conn.getImsCall().isMerged()) {
3390                 // Call was terminated while it is merged instead of a remote disconnect.
3391                 cause = DisconnectCause.IMS_MERGED_SUCCESSFULLY;
3392             }
3393 
3394             EmergencyNumberTracker emergencyNumberTracker = null;
3395             EmergencyNumber num = null;
3396 
3397             if (conn != null && imsCall.getSession() != null) {
3398                 String callId = imsCall.getSession().getCallId();
3399                 emergencyNumberTracker = conn.getEmergencyNumberTracker();
3400                 num = conn.getEmergencyNumberInfo();
3401                 mMetrics.writeOnImsCallTerminated(mPhone.getPhoneId(), imsCall.getCallSession(),
3402                     reasonInfo, mCallQualityMetrics.get(callId), num,
3403                     getNetworkCountryIso(), emergencyNumberTracker != null
3404                         ? emergencyNumberTracker.getEmergencyNumberDbVersion()
3405                         : TelephonyManager.INVALID_EMERGENCY_NUMBER_DB_VERSION);
3406                 mPhone.getVoiceCallSessionStats().onImsCallTerminated(conn, new ImsReasonInfo(
3407                     maybeRemapReasonCode(reasonInfo),
3408                     reasonInfo.mExtraCode, reasonInfo.mExtraMessage));
3409                 // Remove info for the callId from the current calls and add it to the history
3410                 CallQualityMetrics lastCallMetrics = mCallQualityMetrics.remove(callId);
3411                 if (lastCallMetrics != null) {
3412                     mCallQualityMetricsHistory.add(lastCallMetrics);
3413                 }
3414                 pruneCallQualityMetricsHistory();
3415             }
3416             mPhone.notifyImsReason(reasonInfo);
3417 
3418             if (conn != null) {
3419                 conn.setPreciseDisconnectCause(getPreciseDisconnectCauseFromReasonInfo(reasonInfo));
3420                 conn.setImsReasonInfo(reasonInfo);
3421             }
3422 
3423             if (reasonInfo.getCode() == ImsReasonInfo.CODE_SIP_ALTERNATE_EMERGENCY_CALL
3424                     && mAutoRetryFailedWifiEmergencyCall) {
3425                 Pair<ImsCall, ImsReasonInfo> callInfo = new Pair<>(imsCall, reasonInfo);
3426                 mPhone.getDefaultPhone().mCi.registerForOn(ImsPhoneCallTracker.this,
3427                         EVENT_REDIAL_WIFI_E911_CALL, callInfo);
3428                 sendMessageDelayed(obtainMessage(EVENT_REDIAL_WIFI_E911_TIMEOUT, callInfo),
3429                         TIMEOUT_REDIAL_WIFI_E911_MS);
3430                 final ConnectivityManager mgr = (ConnectivityManager) mPhone.getContext()
3431                         .getSystemService(Context.CONNECTIVITY_SERVICE);
3432                 mgr.setAirplaneMode(false);
3433                 return;
3434             } else if (reasonInfo.getCode() == ImsReasonInfo.CODE_RETRY_ON_IMS_WITHOUT_RTT) {
3435                 Pair<ImsCall, ImsReasonInfo> callInfo = new Pair<>(imsCall, reasonInfo);
3436                 sendMessage(obtainMessage(EVENT_REDIAL_WITHOUT_RTT, callInfo));
3437                 return;
3438             } else {
3439                 processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
3440             }
3441 
3442             if (mForegroundCall.getState() != ImsPhoneCall.State.ACTIVE) {
3443                 if (mRingingCall.getState().isRinging()) {
3444                     // Drop pending MO. We should address incoming call first
3445                     mPendingMO = null;
3446                 }
3447             }
3448 
3449             if (mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) {
3450                 if (DBG) {
3451                     log("onCallTerminated: Call terminated in the midst of Switching " +
3452                             "Fg and Bg calls.");
3453                 }
3454                 // If we are the in midst of swapping FG and BG calls and the call that was
3455                 // terminated was the one that we expected to resume, we need to swap the FG and
3456                 // BG calls back.
3457                 if (imsCall == mCallExpectedToResume) {
3458                     if (DBG) {
3459                         log("onCallTerminated: switching " + mForegroundCall + " with "
3460                                 + mBackgroundCall);
3461                     }
3462                     mForegroundCall.switchWith(mBackgroundCall);
3463                 }
3464                 // This call terminated in the midst of a switch after the other call was held, so
3465                 // resume it back to ACTIVE state since the switch failed.
3466                 log("onCallTerminated: foreground call in state " + mForegroundCall.getState() +
3467                         " and ringing call in state " + (mRingingCall == null ? "null" :
3468                         mRingingCall.getState().toString()));
3469 
3470                 sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL);
3471                 mHoldSwitchingState = HoldSwapState.INACTIVE;
3472                 mCallExpectedToResume = null;
3473                 logHoldSwapState("onCallTerminated swap active and hold case");
3474             } else if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_UNHOLD
3475                     || mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_HOLD) {
3476                 mCallExpectedToResume = null;
3477                 mHoldSwitchingState = HoldSwapState.INACTIVE;
3478                 logHoldSwapState("onCallTerminated single call case");
3479             } else if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) {
3480                 // Check to see which call got terminated. If it's the one that was gonna get held,
3481                 // ignore it. If it's the one that was gonna get answered, restore the one that
3482                 // possibly got held.
3483                 if (imsCall == mCallExpectedToResume) {
3484                     mForegroundCall.switchWith(mBackgroundCall);
3485                     mCallExpectedToResume = null;
3486                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3487                     logHoldSwapState("onCallTerminated hold to answer case");
3488                     sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL);
3489                 }
3490             } else if (mHoldSwitchingState == HoldSwapState.HOLDING_TO_DIAL_OUTGOING) {
3491                 // The call that we were gonna hold might've gotten terminated. If that's the case,
3492                 // dial mPendingMo if present.
3493                 if (mPendingMO == null
3494                         || mPendingMO.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) {
3495                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3496                     logHoldSwapState("onCallTerminated hold to dial but no pendingMo");
3497                 } else if (imsCall != mPendingMO.getImsCall()) {
3498                     sendEmptyMessage(EVENT_DIAL_PENDINGMO);
3499                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3500                     logHoldSwapState("onCallTerminated hold to dial, dial pendingMo");
3501                 }
3502             }
3503 
3504             if (mShouldUpdateImsConfigOnDisconnect) {
3505                 // Ensure we update the IMS config when the call is disconnected; we delayed this
3506                 // because a video call was paused.
3507                 updateImsServiceConfig();
3508                 mShouldUpdateImsConfigOnDisconnect = false;
3509             }
3510 
3511             if (mPendingSilentRedialInfo != null) {
3512                 mPhone.initiateSilentRedial(mPendingSilentRedialInfo.first,
3513                                             mPendingSilentRedialInfo.second);
3514                 mPendingSilentRedialInfo = null;
3515             }
3516         }
3517 
3518         @Override
3519         public void onCallHeld(ImsCall imsCall) {
3520             if (DBG) {
3521                 if (mForegroundCall.getImsCall() == imsCall) {
3522                     log("onCallHeld (fg) " + imsCall);
3523                 } else if (mBackgroundCall.getImsCall() == imsCall) {
3524                     log("onCallHeld (bg) " + imsCall);
3525                 }
3526             }
3527 
3528             synchronized (mSyncHold) {
3529                 ImsPhoneCall.State oldState = mBackgroundCall.getState();
3530                 processCallStateChange(imsCall, ImsPhoneCall.State.HOLDING,
3531                         DisconnectCause.NOT_DISCONNECTED);
3532 
3533                 // Note: If we're performing a switchWaitingOrHoldingAndActive, the call to
3534                 // processCallStateChange above may have caused the mBackgroundCall and
3535                 // mForegroundCall references below to change meaning.  Watch out for this if you
3536                 // are reading through this code.
3537                 if (mHoldSwitchingState
3538                         == HoldSwapState.PENDING_RESUME_FOREGROUND_AFTER_HOLD) {
3539                     sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL);
3540                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3541                     mCallExpectedToResume = null;
3542                 } else if (oldState == ImsPhoneCall.State.ACTIVE) {
3543                     // Note: This case comes up when we have just held a call in response to a
3544                     // switchWaitingOrHoldingAndActive.  We now need to resume the background call.
3545                     if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING
3546                             && mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD) {
3547                         sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL);
3548                     } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING
3549                             && mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) {
3550                         sendEmptyMessage(EVENT_ANSWER_WAITING_CALL);
3551                     } else if (mPendingMO != null
3552                             && mHoldSwitchingState == HoldSwapState.HOLDING_TO_DIAL_OUTGOING) {
3553                         dialPendingMO();
3554                         mHoldSwitchingState = HoldSwapState.INACTIVE;
3555                         logHoldSwapState("onCallHeld hold to dial");
3556                     } else {
3557                         // In this case there will be no call resumed, so we can assume that we
3558                         // are done switching fg and bg calls now.
3559                         // This may happen if there is no BG call and we are holding a call so that
3560                         // we can dial another one.
3561                         mHoldSwitchingState = HoldSwapState.INACTIVE;
3562                         logHoldSwapState("onCallHeld normal case");
3563                     }
3564                 } else if (oldState == ImsPhoneCall.State.IDLE
3565                         && (mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD
3566                                 || mHoldSwitchingState
3567                                 == HoldSwapState.HOLDING_TO_ANSWER_INCOMING)) {
3568                     // The other call terminated in the midst of a switch before this call was held,
3569                     // so resume the foreground call back to ACTIVE state since the switch failed.
3570                     if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) {
3571                         sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL);
3572                         mHoldSwitchingState = HoldSwapState.INACTIVE;
3573                         mCallExpectedToResume = null;
3574                         logHoldSwapState("onCallHeld premature termination of other call");
3575                     }
3576                 }
3577             }
3578             mMetrics.writeOnImsCallHeld(mPhone.getPhoneId(), imsCall.getCallSession());
3579         }
3580 
3581         @Override
3582         public void onCallHoldFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
3583             if (DBG) log("onCallHoldFailed reasonCode=" + reasonInfo.getCode());
3584 
3585             synchronized (mSyncHold) {
3586                 ImsPhoneCall.State bgState = mBackgroundCall.getState();
3587                 if (mHoldSwitchingState
3588                         == HoldSwapState.PENDING_RESUME_FOREGROUND_AFTER_HOLD) {
3589                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3590                 } else if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_TERMINATED) {
3591                     // disconnected while processing hold
3592                     if (mPendingMO != null) {
3593                         dialPendingMO();
3594                     } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING
3595                             && mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) {
3596                         sendEmptyMessage(EVENT_ANSWER_WAITING_CALL);
3597                     }
3598                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3599                 } else if (mPendingMO != null && mPendingMO.isEmergency()) {
3600                     // If mPendingMO is an emergency call, disconnect the call that we tried to
3601                     // hold.
3602                     mBackgroundCall.getImsCall().terminate(ImsReasonInfo.CODE_UNSPECIFIED);
3603                     if (imsCall != mCallExpectedToResume) {
3604                         mCallExpectedToResume = null;
3605                     }
3606                     // Leave mHoldSwitchingState as is for now -- we'll reset it
3607                     // in onCallTerminated, which will also dial the outgoing emergency call.
3608                 } else if (mRingingCall.getState() == ImsPhoneCall.State.WAITING
3609                         && mHoldSwitchingState == HoldSwapState.HOLDING_TO_ANSWER_INCOMING) {
3610                     // If we issued a hold request in order to answer an incoming call, we need
3611                     // to tell Telecom that we can't actually answer the incoming call.
3612                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3613                     mForegroundCall.switchWith(mBackgroundCall);
3614                     logHoldSwapState("onCallHoldFailed unable to answer waiting call");
3615                 } else if (bgState == ImsPhoneCall.State.ACTIVE) {
3616                     mForegroundCall.switchWith(mBackgroundCall);
3617 
3618                     if (mPendingMO != null) {
3619                         mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
3620                         sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
3621                     }
3622                     if (imsCall != mCallExpectedToResume) {
3623                         mCallExpectedToResume = null;
3624                     }
3625                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3626                 }
3627                 ImsPhoneConnection conn = findConnection(imsCall);
3628                 if (conn != null && conn.getState() != ImsPhoneCall.State.DISCONNECTED) {
3629                     conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_HOLD_FAILED, null);
3630                 }
3631                 mPhone.notifySuppServiceFailed(Phone.SuppService.HOLD);
3632             }
3633             mMetrics.writeOnImsCallHoldFailed(mPhone.getPhoneId(), imsCall.getCallSession(),
3634                     reasonInfo);
3635         }
3636 
3637         @Override
3638         public void onCallResumed(ImsCall imsCall) {
3639             if (DBG) log("onCallResumed");
3640 
3641             // If we are the in midst of swapping FG and BG calls and the call we end up resuming
3642             // is not the one we expected, we likely had a resume failure and we need to swap the
3643             // FG and BG calls back.
3644             if (mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD
3645                     || mHoldSwitchingState == HoldSwapState.PENDING_RESUME_FOREGROUND_AFTER_FAILURE
3646                     || mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_UNHOLD) {
3647                 if (imsCall != mCallExpectedToResume) {
3648                     // If the call which resumed isn't as expected, we need to swap back to the
3649                     // previous configuration; the swap has failed.
3650                     if (DBG) {
3651                         log("onCallResumed : switching " + mForegroundCall + " with "
3652                                 + mBackgroundCall);
3653                     }
3654                     mForegroundCall.switchWith(mBackgroundCall);
3655                 } else {
3656                     // The call which resumed is the one we expected to resume, so we can clear out
3657                     // the mSwitchingFgAndBgCalls flag.
3658                     if (DBG) {
3659                         log("onCallResumed : expected call resumed.");
3660                     }
3661                 }
3662                 mHoldSwitchingState = HoldSwapState.INACTIVE;
3663                 mCallExpectedToResume = null;
3664                 logHoldSwapState("onCallResumed");
3665             }
3666             processCallStateChange(imsCall, ImsPhoneCall.State.ACTIVE,
3667                     DisconnectCause.NOT_DISCONNECTED);
3668             mMetrics.writeOnImsCallResumed(mPhone.getPhoneId(), imsCall.getCallSession());
3669         }
3670 
3671         @Override
3672         public void onCallResumeFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
3673             if (mHoldSwitchingState == HoldSwapState.SWAPPING_ACTIVE_AND_HELD
3674                     || mHoldSwitchingState
3675                     == HoldSwapState.PENDING_RESUME_FOREGROUND_AFTER_FAILURE) {
3676                 // If we are in the midst of swapping the FG and BG calls and
3677                 // we got a resume fail, we need to swap back the FG and BG calls.
3678                 // Since the FG call was held, will also try to resume the same.
3679                 if (imsCall == mCallExpectedToResume) {
3680                     if (DBG) {
3681                         log("onCallResumeFailed : switching " + mForegroundCall + " with "
3682                                 + mBackgroundCall);
3683                     }
3684                     mForegroundCall.switchWith(mBackgroundCall);
3685                     if (mForegroundCall.getState() == ImsPhoneCall.State.HOLDING) {
3686                         sendEmptyMessage(EVENT_RESUME_NOW_FOREGROUND_CALL);
3687                     }
3688                 }
3689 
3690                 //Call swap is done, reset the relevant variables
3691                 mCallExpectedToResume = null;
3692                 mHoldSwitchingState = HoldSwapState.INACTIVE;
3693                 logHoldSwapState("onCallResumeFailed: multi calls");
3694             } else if (mHoldSwitchingState == HoldSwapState.PENDING_SINGLE_CALL_UNHOLD) {
3695                 if (imsCall == mCallExpectedToResume) {
3696                     if (DBG) {
3697                         log("onCallResumeFailed: single call unhold case");
3698                     }
3699                     mForegroundCall.switchWith(mBackgroundCall);
3700 
3701                     mCallExpectedToResume = null;
3702                     mHoldSwitchingState = HoldSwapState.INACTIVE;
3703                     logHoldSwapState("onCallResumeFailed: single call");
3704                 } else {
3705                     Rlog.w(LOG_TAG, "onCallResumeFailed: got a resume failed for a different call"
3706                             + " in the single call unhold case");
3707                 }
3708             }
3709             mPhone.notifySuppServiceFailed(Phone.SuppService.RESUME);
3710             mMetrics.writeOnImsCallResumeFailed(mPhone.getPhoneId(), imsCall.getCallSession(),
3711                     reasonInfo);
3712         }
3713 
3714         @Override
3715         public void onCallResumeReceived(ImsCall imsCall) {
3716             if (DBG) log("onCallResumeReceived");
3717             ImsPhoneConnection conn = findConnection(imsCall);
3718             if (conn != null) {
3719                 if (mOnHoldToneStarted) {
3720                     mPhone.stopOnHoldTone(conn);
3721                     mOnHoldToneStarted = false;
3722                 }
3723                 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_UNHELD, null);
3724             }
3725 
3726             boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
3727                     com.android.internal.R.bool.config_useVideoPauseWorkaround);
3728             if (useVideoPauseWorkaround && mSupportPauseVideo &&
3729                     VideoProfile.isVideo(conn.getVideoState())) {
3730                 // If we are using the video pause workaround, the vendor IMS code has issues
3731                 // with video pause signalling.  In this case, when a call is remotely
3732                 // held, the modem does not reliably change the video state of the call to be
3733                 // paused.
3734                 // As a workaround, we will turn on that bit now.
3735                 conn.changeToUnPausedState();
3736             }
3737 
3738             SuppServiceNotification supp = new SuppServiceNotification();
3739             // Type of notification: 0 = MO; 1 = MT
3740             // Refer SuppServiceNotification class documentation.
3741             supp.notificationType = 1;
3742             supp.code = SuppServiceNotification.CODE_2_CALL_RETRIEVED;
3743             mPhone.notifySuppSvcNotification(supp);
3744             mMetrics.writeOnImsCallResumeReceived(mPhone.getPhoneId(), imsCall.getCallSession());
3745         }
3746 
3747         @Override
3748         public void onCallHoldReceived(ImsCall imsCall) {
3749             ImsPhoneCallTracker.this.onCallHoldReceived(imsCall);
3750         }
3751 
3752         @Override
3753         public void onCallSuppServiceReceived(ImsCall call,
3754                 ImsSuppServiceNotification suppServiceInfo) {
3755             if (DBG) log("onCallSuppServiceReceived: suppServiceInfo=" + suppServiceInfo);
3756 
3757             SuppServiceNotification supp = new SuppServiceNotification();
3758             supp.notificationType = suppServiceInfo.notificationType;
3759             supp.code = suppServiceInfo.code;
3760             supp.index = suppServiceInfo.index;
3761             supp.number = suppServiceInfo.number;
3762             supp.history = suppServiceInfo.history;
3763 
3764             mPhone.notifySuppSvcNotification(supp);
3765         }
3766 
3767         @Override
3768         public void onCallMerged(final ImsCall call, final ImsCall peerCall, boolean swapCalls) {
3769             if (DBG) log("onCallMerged");
3770 
3771             ImsPhoneCall foregroundImsPhoneCall = findConnection(call).getCall();
3772             ImsPhoneConnection peerConnection = findConnection(peerCall);
3773             ImsPhoneCall peerImsPhoneCall = peerConnection == null ? null
3774                     : peerConnection.getCall();
3775 
3776             if (swapCalls) {
3777                 switchAfterConferenceSuccess();
3778             }
3779             foregroundImsPhoneCall.merge(peerImsPhoneCall, ImsPhoneCall.State.ACTIVE);
3780 
3781             final ImsPhoneConnection conn = findConnection(call);
3782             try {
3783                 log("onCallMerged: ImsPhoneConnection=" + conn);
3784                 log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider());
3785                 setVideoCallProvider(conn, call);
3786                 log("onCallMerged: CurrentVideoProvider=" + conn.getVideoProvider());
3787             } catch (Exception e) {
3788                 loge("onCallMerged: exception " + e);
3789             }
3790 
3791             // After merge complete, update foreground as Active
3792             // and background call as Held, if background call exists
3793             processCallStateChange(mForegroundCall.getImsCall(), ImsPhoneCall.State.ACTIVE,
3794                     DisconnectCause.NOT_DISCONNECTED);
3795             if (peerConnection != null) {
3796                 processCallStateChange(mBackgroundCall.getImsCall(), ImsPhoneCall.State.HOLDING,
3797                     DisconnectCause.NOT_DISCONNECTED);
3798             }
3799 
3800             // Check if the merge was requested by an existing conference call. In that
3801             // case, no further action is required.
3802             if (!call.isMergeRequestedByConf()) {
3803                 log("onCallMerged :: calling onMultipartyStateChanged()");
3804                 onMultipartyStateChanged(call, true);
3805             } else {
3806                 log("onCallMerged :: Merge requested by existing conference.");
3807                 // Reset the flag.
3808                 call.resetIsMergeRequestedByConf(false);
3809             }
3810 
3811             // Notify completion of merge
3812             if (conn != null) {
3813                 conn.handleMergeComplete();
3814             }
3815             logState();
3816         }
3817 
3818         @Override
3819         public void onCallMergeFailed(ImsCall call, ImsReasonInfo reasonInfo) {
3820             if (DBG) log("onCallMergeFailed reasonInfo=" + reasonInfo);
3821 
3822             // TODO: the call to notifySuppServiceFailed throws up the "merge failed" dialog
3823             // We should move this into the InCallService so that it is handled appropriately
3824             // based on the user facing UI.
3825             mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
3826 
3827             call.resetIsMergeRequestedByConf(false);
3828 
3829             // Start plumbing this even through Telecom so other components can take
3830             // appropriate action.
3831             ImsPhoneConnection foregroundConnection = mForegroundCall.getFirstConnection();
3832             if (foregroundConnection != null) {
3833                 foregroundConnection.onConferenceMergeFailed();
3834                 foregroundConnection.handleMergeComplete();
3835             }
3836 
3837             ImsPhoneConnection backgroundConnection = mBackgroundCall.getFirstConnection();
3838             if (backgroundConnection != null) {
3839                 backgroundConnection.onConferenceMergeFailed();
3840                 backgroundConnection.handleMergeComplete();
3841             }
3842         }
3843 
3844         private void updateConferenceParticipantsTiming(List<ConferenceParticipant> participants) {
3845             for (ConferenceParticipant participant : participants) {
3846                 // Every time participants are newly created from parcel, update their connect time.
3847                 CacheEntry cachedConnectTime = findConnectionTimeUsePhoneNumber(participant);
3848                 if (cachedConnectTime != null) {
3849                     participant.setConnectTime(cachedConnectTime.mConnectTime);
3850                     participant.setConnectElapsedTime(cachedConnectTime.mConnectElapsedTime);
3851                     participant.setCallDirection(cachedConnectTime.mCallDirection);
3852                 }
3853             }
3854         }
3855 
3856         /**
3857          * Called when the state of IMS conference participant(s) has changed.
3858          *
3859          * @param call the call object that carries out the IMS call.
3860          * @param participants the participant(s) and their new state information.
3861          */
3862         @Override
3863         public void onConferenceParticipantsStateChanged(ImsCall call,
3864                 List<ConferenceParticipant> participants) {
3865             if (DBG) log("onConferenceParticipantsStateChanged");
3866 
3867             if (!mIsConferenceEventPackageEnabled) {
3868                 logi("onConferenceParticipantsStateChanged - CEP handling disabled");
3869                 return;
3870             }
3871 
3872             if (!mSupportCepOnPeer && !call.isConferenceHost()) {
3873                 logi("onConferenceParticipantsStateChanged - ignore CEP on peer");
3874                 return;
3875             }
3876 
3877             ImsPhoneConnection conn = findConnection(call);
3878             if (conn != null) {
3879                 updateConferenceParticipantsTiming(participants);
3880                 conn.updateConferenceParticipants(participants);
3881             }
3882         }
3883 
3884         @Override
3885         public void onCallSessionTtyModeReceived(ImsCall call, int mode) {
3886             mPhone.onTtyModeReceived(mode);
3887         }
3888 
3889         @Override
3890         public void onCallHandover(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
3891             ImsReasonInfo reasonInfo) {
3892             // Check with the DCTracker to see if data is enabled; there may be a case when
3893             // ImsPhoneCallTracker isn't being informed of the right data enabled state via its
3894             // registration, so we'll refresh now.
3895             boolean isDataEnabled;
3896             if (mPhone.getDefaultPhone().isUsingNewDataStack()) {
3897                 isDataEnabled = mPhone.getDefaultPhone().getDataSettingsManager().isDataEnabled();
3898             } else {
3899                 isDataEnabled = mPhone.getDefaultPhone().getDataEnabledSettings().isDataEnabled();
3900             }
3901 
3902             if (DBG) {
3903                 log("onCallHandover ::  srcAccessTech=" + srcAccessTech + ", targetAccessTech="
3904                         + targetAccessTech + ", reasonInfo=" + reasonInfo + ", dataEnabled="
3905                         + mIsDataEnabled + "/" + isDataEnabled + ", dataMetered="
3906                         + mIsViLteDataMetered);
3907             }
3908             if (mIsDataEnabled != isDataEnabled) {
3909                 loge("onCallHandover: data enabled state doesn't match! (was=" + mIsDataEnabled
3910                         + ", actually=" + isDataEnabled);
3911                 mIsDataEnabled = isDataEnabled;
3912             }
3913 
3914             // Only consider it a valid handover to WIFI if the source radio tech is known.
3915             boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
3916                     && srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
3917                     && targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
3918             // Only consider it a handover from WIFI if the source and target radio tech is known.
3919             boolean isHandoverFromWifi =
3920                     srcAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN
3921                             && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_UNKNOWN
3922                             && targetAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
3923 
3924             ImsPhoneConnection conn = findConnection(imsCall);
3925             if (conn != null) {
3926                 if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
3927                     if (isHandoverToWifi) {
3928                         removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
3929 
3930                         if (mNotifyHandoverVideoFromLTEToWifi && mHasAttemptedStartOfCallHandover) {
3931                             // This is a handover which happened mid-call (ie not the start of call
3932                             // handover from LTE to WIFI), so we'll notify the InCall UI.
3933                             conn.onConnectionEvent(
3934                                     TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_LTE_TO_WIFI, null);
3935                         }
3936 
3937                         // We are on WIFI now so no need to get notified of network availability.
3938                         unregisterForConnectivityChanges();
3939                     } else if (isHandoverFromWifi && imsCall.isVideoCall()) {
3940                         // A video call just dropped from WIFI to LTE; we want to be informed if a
3941                         // new WIFI
3942                         // network comes into range.
3943                         registerForConnectivityChanges();
3944                     }
3945                 }
3946 
3947                 if (isHandoverToWifi && mIsViLteDataMetered) {
3948                     conn.setLocalVideoCapable(true);
3949                 }
3950 
3951                 if (isHandoverFromWifi && imsCall.isVideoCall()) {
3952                     if (mIsViLteDataMetered) {
3953                         conn.setLocalVideoCapable(mIsDataEnabled);
3954                     }
3955 
3956                     if (mNotifyHandoverVideoFromWifiToLTE && mIsDataEnabled) {
3957                         if (conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
3958                             log("onCallHandover :: notifying of WIFI to LTE handover.");
3959                             conn.onConnectionEvent(
3960                                     TelephonyManager.EVENT_HANDOVER_VIDEO_FROM_WIFI_TO_LTE, null);
3961                         } else {
3962                             // Call has already had a disconnect request issued by the user or is
3963                             // in the process of disconnecting; do not inform the UI of this as it
3964                             // is not relevant.
3965                             log("onCallHandover :: skip notify of WIFI to LTE handover for "
3966                                     + "disconnected call.");
3967                         }
3968                     }
3969 
3970                     if (!mIsDataEnabled && mIsViLteDataMetered) {
3971                         // Call was downgraded from WIFI to LTE and data is metered; downgrade the
3972                         // call now.
3973                         log("onCallHandover :: data is not enabled; attempt to downgrade.");
3974                         downgradeVideoCall(ImsReasonInfo.CODE_WIFI_LOST, conn);
3975                     }
3976                 }
3977             } else {
3978                 loge("onCallHandover :: connection null.");
3979             }
3980             // If there's a handover, then we're not in the "start of call" handover phase.
3981             if (!mHasAttemptedStartOfCallHandover) {
3982                 mHasAttemptedStartOfCallHandover = true;
3983             }
3984             mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(),
3985                     TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER, imsCall.getCallSession(),
3986                     srcAccessTech, targetAccessTech, reasonInfo);
3987         }
3988 
3989         @Override
3990         public void onCallHandoverFailed(ImsCall imsCall, int srcAccessTech, int targetAccessTech,
3991             ImsReasonInfo reasonInfo) {
3992             if (DBG) {
3993                 log("onCallHandoverFailed :: srcAccessTech=" + srcAccessTech +
3994                     ", targetAccessTech=" + targetAccessTech + ", reasonInfo=" + reasonInfo);
3995             }
3996             mMetrics.writeOnImsCallHandoverEvent(mPhone.getPhoneId(),
3997                     TelephonyCallSession.Event.Type.IMS_CALL_HANDOVER_FAILED,
3998                     imsCall.getCallSession(), srcAccessTech, targetAccessTech, reasonInfo);
3999 
4000             boolean isHandoverToWifi = srcAccessTech != ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN &&
4001                     targetAccessTech == ServiceState.RIL_RADIO_TECHNOLOGY_IWLAN;
4002             ImsPhoneConnection conn = findConnection(imsCall);
4003             if (conn != null && isHandoverToWifi) {
4004                 log("onCallHandoverFailed - handover to WIFI Failed");
4005 
4006                 // If we know we failed to handover, don't check for failure in the future.
4007                 removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
4008 
4009                 if (imsCall.isVideoCall()
4010                         && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
4011                     // Start listening for a WIFI network to come into range for potential handover.
4012                     registerForConnectivityChanges();
4013                 }
4014 
4015                 if (mNotifyVtHandoverToWifiFail) {
4016                     // Only notify others if carrier config indicates to do so.
4017                     conn.onHandoverToWifiFailed();
4018                 }
4019             }
4020             if (!mHasAttemptedStartOfCallHandover) {
4021                 mHasAttemptedStartOfCallHandover = true;
4022             }
4023         }
4024 
4025         @Override
4026         public void onRttModifyRequestReceived(ImsCall imsCall) {
4027             ImsPhoneConnection conn = findConnection(imsCall);
4028             if (conn != null) {
4029                 conn.onRttModifyRequestReceived();
4030             }
4031         }
4032 
4033         @Override
4034         public void onRttModifyResponseReceived(ImsCall imsCall, int status) {
4035             ImsPhoneConnection conn = findConnection(imsCall);
4036             if (conn != null) {
4037                 conn.onRttModifyResponseReceived(status);
4038             }
4039         }
4040 
4041         @Override
4042         public void onRttMessageReceived(ImsCall imsCall, String message) {
4043             ImsPhoneConnection conn = findConnection(imsCall);
4044             if (conn != null) {
4045                 conn.onRttMessageReceived(message);
4046             }
4047         }
4048 
4049         @Override
4050         public void onRttAudioIndicatorChanged(ImsCall imsCall, ImsStreamMediaProfile profile) {
4051           ImsPhoneConnection conn = findConnection(imsCall);
4052             if (conn != null) {
4053                 conn.onRttAudioIndicatorChanged(profile);
4054             }
4055         }
4056 
4057         @Override
4058         public void onCallSessionTransferred(ImsCall imsCall) {
4059             if (DBG) log("onCallSessionTransferred success");
4060         }
4061 
4062         @Override
4063         public void onCallSessionTransferFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
4064             if (DBG) log("onCallSessionTransferFailed reasonInfo=" + reasonInfo);
4065             mPhone.notifySuppServiceFailed(Phone.SuppService.TRANSFER);
4066         }
4067 
4068         @Override
4069         public void onCallSessionDtmfReceived(ImsCall imsCall, char digit) {
4070             log("onCallSessionDtmfReceived digit=" + digit);
4071             ImsPhoneConnection conn = findConnection(imsCall);
4072             if (conn != null) {
4073                 conn.receivedDtmfDigit(digit);
4074             }
4075         }
4076 
4077         /**
4078          * Handles a change to the multiparty state for an {@code ImsCall}.  Notifies the associated
4079          * {@link ImsPhoneConnection} of the change.
4080          *
4081          * @param imsCall The IMS call.
4082          * @param isMultiParty {@code true} if the call became multiparty, {@code false}
4083          *      otherwise.
4084          */
4085         @Override
4086         public void onMultipartyStateChanged(ImsCall imsCall, boolean isMultiParty) {
4087             if (DBG) log("onMultipartyStateChanged to " + (isMultiParty ? "Y" : "N"));
4088 
4089             ImsPhoneConnection conn = findConnection(imsCall);
4090             if (conn != null) {
4091                 conn.updateMultipartyState(isMultiParty);
4092                 mPhone.getVoiceCallSessionStats().onMultipartyChange(conn, isMultiParty);
4093             }
4094         }
4095 
4096         /**
4097          * Handles a change to the call quality for an {@code ImsCall}.
4098          * Notifies apps through the System API {@link PhoneStateListener#onCallAttributesChanged}.
4099          */
4100         @Override
4101         public void onCallQualityChanged(ImsCall imsCall, CallQuality callQuality) {
4102             // convert ServiceState.radioTech to TelephonyManager.NetworkType constant
4103             mPhone.onCallQualityChanged(callQuality, imsCall.getNetworkType());
4104             String callId = imsCall.getSession().getCallId();
4105             CallQualityMetrics cqm = mCallQualityMetrics.get(callId);
4106             if (cqm == null) {
4107                 cqm = new CallQualityMetrics(mPhone);
4108             }
4109             cqm.saveCallQuality(callQuality);
4110             mCallQualityMetrics.put(callId, cqm);
4111 
4112             ImsPhoneConnection conn = findConnection(imsCall);
4113             if (conn != null) {
4114                 Bundle report = new Bundle();
4115                 report.putParcelable(android.telecom.Connection.EXTRA_CALL_QUALITY_REPORT,
4116                         callQuality);
4117                 conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_QUALITY_REPORT,
4118                         report);
4119             }
4120         }
4121 
4122         /**
4123          * Handles reception of RTP header extension data from the network.
4124          * @param imsCall The ImsCall the data was received on.
4125          * @param rtpHeaderExtensionData The RTP extension data received.
4126          */
4127         @Override
4128         public void onCallSessionRtpHeaderExtensionsReceived(ImsCall imsCall,
4129                 @NonNull Set<RtpHeaderExtension> rtpHeaderExtensionData) {
4130             log("onCallSessionRtpHeaderExtensionsReceived numExtensions="
4131                     + rtpHeaderExtensionData.size());
4132             ImsPhoneConnection conn = findConnection(imsCall);
4133             if (conn != null) {
4134                 conn.receivedRtpHeaderExtensions(rtpHeaderExtensionData);
4135             }
4136         }
4137     };
4138 
4139     /**
4140      * Listen to the IMS call state change
4141      */
4142     private ImsCall.Listener mImsUssdListener = new ImsCall.Listener() {
4143         @Override
4144         public void onCallStarted(ImsCall imsCall) {
4145             if (DBG) log("mImsUssdListener onCallStarted");
4146 
4147             if (imsCall == mUssdSession) {
4148                 if (mPendingUssd != null) {
4149                     AsyncResult.forMessage(mPendingUssd);
4150                     mPendingUssd.sendToTarget();
4151                     mPendingUssd = null;
4152                 }
4153             }
4154         }
4155 
4156         @Override
4157         public void onCallStartFailed(ImsCall imsCall, ImsReasonInfo reasonInfo) {
4158             if (DBG) log("mImsUssdListener onCallStartFailed reasonCode=" + reasonInfo.getCode());
4159 
4160             if (mUssdSession != null) {
4161                 if (DBG) log("mUssdSession is not null");
4162                 // To initiate sending Ussd under circuit-switched call
4163                 if (reasonInfo.getCode() == ImsReasonInfo.CODE_LOCAL_CALL_CS_RETRY_REQUIRED
4164                         && mUssdMethod != USSD_OVER_IMS_ONLY) {
4165                     mUssdSession = null;
4166                     mPhone.getPendingMmiCodes().clear();
4167                     mPhone.initiateSilentRedial();
4168                     if (DBG) log("Initiated sending ussd by using silent redial.");
4169                     return;
4170                 } else {
4171                     if (DBG) log("Failed to start sending ussd by using silent resendUssd.!!");
4172                 }
4173             }
4174 
4175             onCallTerminated(imsCall, reasonInfo);
4176         }
4177 
4178         @Override
4179         public void onCallTerminated(ImsCall imsCall, ImsReasonInfo reasonInfo) {
4180             if (DBG) log("mImsUssdListener onCallTerminated reasonCode=" + reasonInfo.getCode());
4181             removeMessages(EVENT_CHECK_FOR_WIFI_HANDOVER);
4182             mHasAttemptedStartOfCallHandover = false;
4183             unregisterForConnectivityChanges();
4184 
4185             if (imsCall == mUssdSession) {
4186                 mUssdSession = null;
4187                 if (mPendingUssd != null) {
4188                     CommandException ex =
4189                             new CommandException(CommandException.Error.GENERIC_FAILURE);
4190                     AsyncResult.forMessage(mPendingUssd, null, ex);
4191                     mPendingUssd.sendToTarget();
4192                     mPendingUssd = null;
4193                 }
4194             }
4195             imsCall.close();
4196         }
4197 
4198         @Override
4199         public void onCallUssdMessageReceived(ImsCall call,
4200                 int mode, String ussdMessage) {
4201             if (DBG) log("mImsUssdListener onCallUssdMessageReceived mode=" + mode);
4202 
4203             int ussdMode = -1;
4204 
4205             switch(mode) {
4206                 case ImsCall.USSD_MODE_REQUEST:
4207                     ussdMode = CommandsInterface.USSD_MODE_REQUEST;
4208                     break;
4209 
4210                 case ImsCall.USSD_MODE_NOTIFY:
4211                     ussdMode = CommandsInterface.USSD_MODE_NOTIFY;
4212                     break;
4213             }
4214 
4215             mPhone.onIncomingUSSD(ussdMode, ussdMessage);
4216         }
4217     };
4218 
4219     private final ImsMmTelManager.CapabilityCallback mImsCapabilityCallback =
4220             new ImsMmTelManager.CapabilityCallback() {
4221                 @Override
4222                 public void onCapabilitiesStatusChanged(
4223                         MmTelFeature.MmTelCapabilities capabilities) {
4224                     if (DBG) log("onCapabilitiesStatusChanged: " + capabilities);
4225                     SomeArgs args = SomeArgs.obtain();
4226                     args.arg1 = capabilities;
4227                     // Remove any pending updates; they're already stale, so no need to process
4228                     // them.
4229                     removeMessages(EVENT_ON_FEATURE_CAPABILITY_CHANGED);
4230                     obtainMessage(EVENT_ON_FEATURE_CAPABILITY_CHANGED, args).sendToTarget();
4231                 }
4232             };
4233 
4234     private final ImsManager.ImsStatsCallback mImsStatsCallback =
4235             new ImsManager.ImsStatsCallback() {
4236         @Override
4237         public void onEnabledMmTelCapabilitiesChanged(int capability, int regTech,
4238                 boolean isEnabled) {
4239             int enabledVal = isEnabled ? ProvisioningManager.PROVISIONING_VALUE_ENABLED
4240                     : ProvisioningManager.PROVISIONING_VALUE_DISABLED;
4241             mMetrics.writeImsSetFeatureValue(mPhone.getPhoneId(), capability, regTech, enabledVal);
4242             mPhone.getImsStats().onSetFeatureResponse(capability, regTech, enabledVal);
4243         }
4244     };
4245 
4246     private final ProvisioningManager.Callback mConfigCallback =
4247             new ProvisioningManager.Callback() {
4248         @Override
4249         public void onProvisioningIntChanged(int item, int value) {
4250             sendConfigChangedIntent(item, Integer.toString(value));
4251             if ((mImsManager != null)
4252                     && (item == ImsConfig.ConfigConstants.VOICE_OVER_WIFI_SETTING_ENABLED
4253                     || item == ImsConfig.ConfigConstants.VLT_SETTING_ENABLED
4254                     || item == ImsConfig.ConfigConstants.LVC_SETTING_ENABLED)) {
4255                 // Update Ims Service state to make sure updated provisioning values take effect
4256                 // immediately.
4257                 updateImsServiceConfig();
4258             }
4259         }
4260 
4261         @Override
4262         public void onProvisioningStringChanged(int item, String value) {
4263             sendConfigChangedIntent(item, value);
4264         }
4265 
4266         // send IMS_CONFIG_CHANGED intent for older services that do not implement the new callback
4267         // interface.
4268         private void sendConfigChangedIntent(int item, String value) {
4269             log("sendConfigChangedIntent - [" + item + ", " + value + "]");
4270             Intent configChangedIntent = new Intent(ImsConfig.ACTION_IMS_CONFIG_CHANGED);
4271             configChangedIntent.putExtra(ImsConfig.EXTRA_CHANGED_ITEM, item);
4272             configChangedIntent.putExtra(ImsConfig.EXTRA_NEW_VALUE, value);
4273             if (mPhone != null && mPhone.getContext() != null) {
4274                 mPhone.getContext().sendBroadcast(configChangedIntent);
4275             }
4276         }
4277     };
4278 
sendCallStartFailedDisconnect(ImsCall imsCall, ImsReasonInfo reasonInfo)4279     public void sendCallStartFailedDisconnect(ImsCall imsCall, ImsReasonInfo reasonInfo) {
4280         mPendingMO = null;
4281         ImsPhoneConnection conn = findConnection(imsCall);
4282         Call.State callState;
4283         if (conn != null) {
4284             callState = conn.getState();
4285         } else {
4286             // Need to fall back in case connection is null; it shouldn't be, but a sane
4287             // fallback is to assume we're dialing.  This state is only used to
4288             // determine which disconnect string to show in the case of a low battery
4289             // disconnect.
4290             callState = Call.State.DIALING;
4291         }
4292         int cause = getDisconnectCauseFromReasonInfo(reasonInfo, callState);
4293 
4294         processCallStateChange(imsCall, ImsPhoneCall.State.DISCONNECTED, cause);
4295 
4296         if (conn != null) {
4297             conn.setPreciseDisconnectCause(
4298                     getPreciseDisconnectCauseFromReasonInfo(reasonInfo));
4299         }
4300 
4301         mPhone.notifyImsReason(reasonInfo);
4302     }
4303 
4304     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
getUtInterface()4305     public ImsUtInterface getUtInterface() throws ImsException {
4306         if (mImsManager == null) {
4307             throw getImsManagerIsNullException();
4308         }
4309 
4310         ImsUtInterface ut = mImsManager.createOrGetSupplementaryServiceConfiguration();
4311         return ut;
4312     }
4313 
transferHandoverConnections(ImsPhoneCall call)4314     private void transferHandoverConnections(ImsPhoneCall call) {
4315         if (call.getConnections() != null) {
4316             for (Connection c : call.getConnections()) {
4317                 c.mPreHandoverState = call.mState;
4318                 log ("Connection state before handover is " + c.getStateBeforeHandover());
4319             }
4320         }
4321         if (mHandoverCall.getConnections() == null) {
4322             mHandoverCall.mConnections = call.mConnections;
4323         } else { // Multi-call SRVCC
4324             mHandoverCall.mConnections.addAll(call.mConnections);
4325         }
4326         mHandoverCall.copyConnectionFrom(call);
4327         if (mHandoverCall.getConnections() != null) {
4328             if (call.getImsCall() != null) {
4329                 call.getImsCall().close();
4330             }
4331             for (Connection c : mHandoverCall.getConnections()) {
4332                 ((ImsPhoneConnection)c).changeParent(mHandoverCall);
4333                 ((ImsPhoneConnection)c).releaseWakeLock();
4334             }
4335         }
4336         if (call.getState().isAlive()) {
4337             log ("Call is alive and state is " + call.mState);
4338             mHandoverCall.mState = call.mState;
4339         }
4340         call.clearConnections();
4341         call.mState = ImsPhoneCall.State.IDLE;
4342         if (mPendingMO != null) {
4343             // If the call is handed over before moving to alerting (i.e. e911 CSFB redial), clear
4344             // pending MO here.
4345             logi("pending MO on handover, clearing...");
4346             mPendingMO = null;
4347         }
4348     }
4349 
4350     /**
4351      * Notify of a change to SRVCC state
4352      * @param state the new SRVCC state.
4353      */
notifySrvccState(Call.SrvccState state)4354     public void notifySrvccState(Call.SrvccState state) {
4355         if (DBG) log("notifySrvccState state=" + state);
4356 
4357         mSrvccState = state;
4358 
4359         if (mSrvccState == Call.SrvccState.COMPLETED) {
4360             // If the dialing call had ringback, ensure it stops now, otherwise it'll keep playing
4361             // afer the SRVCC completes.
4362             mForegroundCall.maybeStopRingback();
4363 
4364             resetState();
4365             transferHandoverConnections(mForegroundCall);
4366             transferHandoverConnections(mBackgroundCall);
4367             transferHandoverConnections(mRingingCall);
4368             updatePhoneState();
4369         }
4370     }
4371 
resetState()4372     private void resetState() {
4373         mIsInEmergencyCall = false;
4374         mPhone.setEcmCanceledForEmergency(false);
4375         mHoldSwitchingState = HoldSwapState.INACTIVE;
4376     }
4377 
4378     @VisibleForTesting
isHoldOrSwapInProgress()4379     public boolean isHoldOrSwapInProgress() {
4380         return mHoldSwitchingState != HoldSwapState.INACTIVE;
4381     }
4382 
4383     //****** Overridden from Handler
4384 
4385     @Override
4386     public void
handleMessage(Message msg)4387     handleMessage (Message msg) {
4388         AsyncResult ar;
4389         if (DBG) log("handleMessage what=" + msg.what);
4390 
4391         switch (msg.what) {
4392             case EVENT_HANGUP_PENDINGMO:
4393                 if (mPendingMO != null) {
4394                     mPendingMO.onDisconnect();
4395                     removeConnection(mPendingMO);
4396                     mPendingMO = null;
4397                 }
4398                 mPendingIntentExtras = null;
4399                 updatePhoneState();
4400                 mPhone.notifyPreciseCallStateChanged();
4401                 break;
4402             case EVENT_RESUME_NOW_FOREGROUND_CALL:
4403                 try {
4404                     resumeForegroundCall();
4405                 } catch (ImsException e) {
4406                     if (Phone.DEBUG_PHONE) {
4407                         loge("handleMessage EVENT_RESUME_NOW_FOREGROUND_CALL exception=" + e);
4408                     }
4409                 }
4410                 break;
4411             case EVENT_ANSWER_WAITING_CALL:
4412                 try {
4413                     answerWaitingCall();
4414                 } catch (ImsException e) {
4415                     if (Phone.DEBUG_PHONE) {
4416                         loge("handleMessage EVENT_ANSWER_WAITING_CALL exception=" + e);
4417                     }
4418                 }
4419                 break;
4420             case EVENT_DIAL_PENDINGMO:
4421                 dialInternal(mPendingMO, mClirMode, mPendingCallVideoState, mPendingIntentExtras);
4422                 mPendingIntentExtras = null;
4423                 break;
4424 
4425             case EVENT_EXIT_ECBM_BEFORE_PENDINGMO:
4426                 if (mPendingMO != null) {
4427                     //Send ECBM exit request
4428                     try {
4429                         getEcbmInterface().exitEmergencyCallbackMode();
4430                         mPhone.setOnEcbModeExitResponse(this, EVENT_EXIT_ECM_RESPONSE_CDMA, null);
4431                         pendingCallClirMode = mClirMode;
4432                         pendingCallInEcm = true;
4433                     } catch (ImsException e) {
4434                         e.printStackTrace();
4435                         mPendingMO.setDisconnectCause(DisconnectCause.ERROR_UNSPECIFIED);
4436                         sendEmptyMessageDelayed(EVENT_HANGUP_PENDINGMO, TIMEOUT_HANGUP_PENDINGMO);
4437                     }
4438                 }
4439                 break;
4440 
4441             case EVENT_EXIT_ECM_RESPONSE_CDMA:
4442                 // no matter the result, we still do the same here
4443                 if (pendingCallInEcm) {
4444                     dialInternal(mPendingMO, pendingCallClirMode,
4445                             mPendingCallVideoState, mPendingIntentExtras);
4446                     mPendingIntentExtras = null;
4447                     pendingCallInEcm = false;
4448                 }
4449                 mPhone.unsetOnEcbModeExitResponse(this);
4450                 break;
4451             case EVENT_VT_DATA_USAGE_UPDATE:
4452                 ar = (AsyncResult) msg.obj;
4453                 ImsCall call = (ImsCall) ar.userObj;
4454                 Long usage = (long) ar.result;
4455                 log("VT data usage update. usage = " + usage + ", imsCall = " + call);
4456                 if (usage > 0) {
4457                     updateVtDataUsage(call, usage);
4458                 }
4459                 break;
4460             case EVENT_DATA_ENABLED_CHANGED:
4461                 ar = (AsyncResult) msg.obj;
4462                 if (ar.result instanceof Pair) {
4463                     Pair<Boolean, Integer> p = (Pair<Boolean, Integer>) ar.result;
4464                     onDataEnabledChanged(p.first, p.second);
4465                 }
4466                 break;
4467             case EVENT_CHECK_FOR_WIFI_HANDOVER:
4468                 if (msg.obj instanceof ImsCall) {
4469                     ImsCall imsCall = (ImsCall) msg.obj;
4470                     if (imsCall != mForegroundCall.getImsCall()) {
4471                         Rlog.i(LOG_TAG, "handoverCheck: no longer FG; check skipped.");
4472                         unregisterForConnectivityChanges();
4473                         // Handover check and its not the foreground call any more.
4474                         return;
4475                     }
4476                     if (!mHasAttemptedStartOfCallHandover) {
4477                         mHasAttemptedStartOfCallHandover = true;
4478                     }
4479                     if (!imsCall.isWifiCall()) {
4480                         // Call did not handover to wifi, notify of handover failure.
4481                         ImsPhoneConnection conn = findConnection(imsCall);
4482                         if (conn != null) {
4483                             Rlog.i(LOG_TAG, "handoverCheck: handover failed.");
4484                             conn.onHandoverToWifiFailed();
4485                         }
4486 
4487                         if (imsCall.isVideoCall()
4488                                 && conn.getDisconnectCause() == DisconnectCause.NOT_DISCONNECTED) {
4489                             registerForConnectivityChanges();
4490                         }
4491                     }
4492                 }
4493                 break;
4494             case EVENT_ON_FEATURE_CAPABILITY_CHANGED: {
4495                 SomeArgs args = (SomeArgs) msg.obj;
4496                 try {
4497                     ImsFeature.Capabilities capabilities = (ImsFeature.Capabilities) args.arg1;
4498                     handleFeatureCapabilityChanged(capabilities);
4499                 } finally {
4500                     args.recycle();
4501                 }
4502                 break;
4503             }
4504             case EVENT_SUPP_SERVICE_INDICATION: {
4505                 ar = (AsyncResult) msg.obj;
4506                 ImsPhoneMmiCode mmiCode = new ImsPhoneMmiCode(mPhone);
4507                 try {
4508                     mmiCode.setIsSsInfo(true);
4509                     mmiCode.processImsSsData(ar);
4510                 } catch (ImsException e) {
4511                     Rlog.e(LOG_TAG, "Exception in parsing SS Data: " + e);
4512                 }
4513                 break;
4514             }
4515             case EVENT_REDIAL_WIFI_E911_CALL: {
4516                 Pair<ImsCall, ImsReasonInfo> callInfo =
4517                         (Pair<ImsCall, ImsReasonInfo>) ((AsyncResult) msg.obj).userObj;
4518                 removeMessages(EVENT_REDIAL_WIFI_E911_TIMEOUT);
4519                 mPhone.getDefaultPhone().mCi.unregisterForOn(this);
4520                 ImsPhoneConnection oldConnection = findConnection(callInfo.first);
4521                 if (oldConnection == null) {
4522                     sendCallStartFailedDisconnect(callInfo.first, callInfo.second);
4523                     break;
4524                 }
4525                 mForegroundCall.detach(oldConnection);
4526                 removeConnection(oldConnection);
4527                 try {
4528                     Connection newConnection =
4529                             mPhone.getDefaultPhone().dial(mLastDialString, mLastDialArgs);
4530                     oldConnection.onOriginalConnectionReplaced(newConnection);
4531 
4532                     final ImsCall imsCall = mForegroundCall.getImsCall();
4533                     final ImsCallProfile callProfile = imsCall.getCallProfile();
4534                     /* update EXTRA_EMERGENCY_CALL for clients to infer
4535                        from this extra that the call is emergency call */
4536                     callProfile.setCallExtraBoolean(
4537                             ImsCallProfile.EXTRA_EMERGENCY_CALL, true);
4538                     ImsPhoneConnection conn = findConnection(imsCall);
4539                     conn.updateExtras(imsCall);
4540                 } catch (CallStateException e) {
4541                     sendCallStartFailedDisconnect(callInfo.first, callInfo.second);
4542                 }
4543                 break;
4544             }
4545             case EVENT_REDIAL_WIFI_E911_TIMEOUT: {
4546                 Pair<ImsCall, ImsReasonInfo> callInfo = (Pair<ImsCall, ImsReasonInfo>) msg.obj;
4547                 mPhone.getDefaultPhone().mCi.unregisterForOn(this);
4548                 removeMessages(EVENT_REDIAL_WIFI_E911_CALL);
4549                 sendCallStartFailedDisconnect(callInfo.first, callInfo.second);
4550                 break;
4551             }
4552 
4553             case EVENT_REDIAL_WITHOUT_RTT: {
4554                 Pair<ImsCall, ImsReasonInfo> callInfo = (Pair<ImsCall, ImsReasonInfo>) msg.obj;
4555                 removeMessages(EVENT_REDIAL_WITHOUT_RTT);
4556                 ImsPhoneConnection oldConnection = findConnection(callInfo.first);
4557                 if (oldConnection == null) {
4558                     sendCallStartFailedDisconnect(callInfo.first, callInfo.second);
4559                     break;
4560                 }
4561                 mForegroundCall.detach(oldConnection);
4562                 removeConnection(oldConnection);
4563                 try {
4564                     mPendingMO = null;
4565                     ImsDialArgs newDialArgs = ImsDialArgs.Builder.from(mLastDialArgs)
4566                             .setRttTextStream(null)
4567                             .setRetryCallFailCause(ImsReasonInfo.CODE_RETRY_ON_IMS_WITHOUT_RTT)
4568                             .setRetryCallFailNetworkType(
4569                                     ServiceState.rilRadioTechnologyToNetworkType(
4570                                     oldConnection.getCallRadioTech()))
4571                             .build();
4572 
4573                     Connection newConnection =
4574                             mPhone.getDefaultPhone().dial(mLastDialString, newDialArgs);
4575                     oldConnection.onOriginalConnectionReplaced(newConnection);
4576                 } catch (CallStateException e) {
4577                     sendCallStartFailedDisconnect(callInfo.first, callInfo.second);
4578                 }
4579                 break;
4580             }
4581         }
4582     }
4583 
4584     /**
4585      * Update video call data usage
4586      *
4587      * @param call The IMS call
4588      * @param dataUsage The aggregated data usage for the call
4589      */
4590     @VisibleForTesting(visibility = PRIVATE)
updateVtDataUsage(ImsCall call, long dataUsage)4591     public void updateVtDataUsage(ImsCall call, long dataUsage) {
4592         long oldUsage = 0L;
4593         if (mVtDataUsageMap.containsKey(call.uniqueId)) {
4594             oldUsage = mVtDataUsageMap.get(call.uniqueId);
4595         }
4596 
4597         long delta = dataUsage - oldUsage;
4598         mVtDataUsageMap.put(call.uniqueId, dataUsage);
4599 
4600         log("updateVtDataUsage: call=" + call + ", delta=" + delta);
4601 
4602         long currentTime = SystemClock.elapsedRealtime();
4603         int isRoaming = mPhone.getServiceState().getDataRoaming() ? 1 : 0;
4604 
4605         // Create the snapshot of total video call data usage.
4606         NetworkStats vtDataUsageSnapshot = new NetworkStats(currentTime, 1);
4607         vtDataUsageSnapshot = vtDataUsageSnapshot.add(mVtDataUsageSnapshot);
4608         // Since the modem only reports the total vt data usage rather than rx/tx separately,
4609         // the only thing we can do here is splitting the usage into half rx and half tx.
4610         // Uid -1 indicates this is for the overall device data usage.
4611         mVtDataUsageSnapshot = vtDataUsageSnapshot.addEntry(new NetworkStats.Entry(
4612                 getVtInterface(), -1, NetworkStats.SET_FOREGROUND,
4613                 NetworkStats.TAG_NONE, NetworkStats.METERED_YES, isRoaming,
4614                 NetworkStats.DEFAULT_NETWORK_YES, delta / 2, 0, delta / 2, 0, 0));
4615 
4616         // Create the snapshot of video call data usage per dialer. combineValues will create
4617         // a separate entry if uid is different from the previous snapshot.
4618         NetworkStats vtDataUsageUidSnapshot = new NetworkStats(currentTime, 1);
4619         vtDataUsageUidSnapshot = vtDataUsageUidSnapshot.add(mVtDataUsageUidSnapshot);
4620 
4621         // The dialer uid might not be initialized correctly during boot up due to telecom service
4622         // not ready or its default dialer cache not ready. So we double check again here to see if
4623         // default dialer uid is really not available.
4624         if (mDefaultDialerUid.get() == NetworkStats.UID_ALL) {
4625             final TelecomManager telecomManager =
4626                     (TelecomManager) mPhone.getContext().getSystemService(Context.TELECOM_SERVICE);
4627             mDefaultDialerUid.set(
4628                     getPackageUid(mPhone.getContext(), telecomManager.getDefaultDialerPackage()));
4629         }
4630 
4631         // Since the modem only reports the total vt data usage rather than rx/tx separately,
4632         // the only thing we can do here is splitting the usage into half rx and half tx.
4633         mVtDataUsageUidSnapshot = vtDataUsageUidSnapshot.addEntry(new NetworkStats.Entry(
4634                 getVtInterface(), mDefaultDialerUid.get(),
4635                 NetworkStats.SET_FOREGROUND, NetworkStats.TAG_NONE, NetworkStats.METERED_YES,
4636                 isRoaming, NetworkStats.DEFAULT_NETWORK_YES, delta / 2, 0, delta / 2, 0, 0));
4637     }
4638 
4639     @VisibleForTesting(visibility = PRIVATE)
getVtInterface()4640     public String getVtInterface() {
4641         return NetworkStats.IFACE_VT + mPhone.getSubId();
4642     }
4643 
4644     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
4645     @Override
log(String msg)4646     protected void log(String msg) {
4647         Rlog.d(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
4648     }
4649 
4650     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
loge(String msg)4651     protected void loge(String msg) {
4652         Rlog.e(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
4653     }
4654 
logw(String msg)4655     void logw(String msg) {
4656         Rlog.w(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
4657     }
4658 
logi(String msg)4659     void logi(String msg) {
4660         Rlog.i(LOG_TAG, "[" + mPhone.getPhoneId() + "] " + msg);
4661     }
4662 
logHoldSwapState(String loc)4663     void logHoldSwapState(String loc) {
4664         String holdSwapState = "???";
4665         switch (mHoldSwitchingState) {
4666             case INACTIVE:
4667                 holdSwapState = "INACTIVE";
4668                 break;
4669             case PENDING_SINGLE_CALL_HOLD:
4670                 holdSwapState = "PENDING_SINGLE_CALL_HOLD";
4671                 break;
4672             case PENDING_SINGLE_CALL_UNHOLD:
4673                 holdSwapState = "PENDING_SINGLE_CALL_UNHOLD";
4674                 break;
4675             case SWAPPING_ACTIVE_AND_HELD:
4676                 holdSwapState = "SWAPPING_ACTIVE_AND_HELD";
4677                 break;
4678             case HOLDING_TO_ANSWER_INCOMING:
4679                 holdSwapState = "HOLDING_TO_ANSWER_INCOMING";
4680                 break;
4681             case PENDING_RESUME_FOREGROUND_AFTER_FAILURE:
4682                 holdSwapState = "PENDING_RESUME_FOREGROUND_AFTER_FAILURE";
4683                 break;
4684             case HOLDING_TO_DIAL_OUTGOING:
4685                 holdSwapState = "HOLDING_TO_DIAL_OUTGOING";
4686                 break;
4687             case PENDING_RESUME_FOREGROUND_AFTER_HOLD:
4688                 holdSwapState = "PENDING_RESUME_FOREGROUND_AFTER_HOLD";
4689                 break;
4690         }
4691         logi("holdSwapState set to " + holdSwapState + " at " + loc);
4692     }
4693 
4694     /**
4695      * Logs the current state of the ImsPhoneCallTracker.  Useful for debugging issues with
4696      * call tracking.
4697      */
4698     /* package */
logState()4699     void logState() {
4700         if (!VERBOSE_STATE_LOGGING) {
4701             return;
4702         }
4703 
4704         StringBuilder sb = new StringBuilder();
4705         sb.append("Current IMS PhoneCall State:\n");
4706         sb.append(" Foreground: ");
4707         sb.append(mForegroundCall);
4708         sb.append("\n");
4709         sb.append(" Background: ");
4710         sb.append(mBackgroundCall);
4711         sb.append("\n");
4712         sb.append(" Ringing: ");
4713         sb.append(mRingingCall);
4714         sb.append("\n");
4715         sb.append(" Handover: ");
4716         sb.append(mHandoverCall);
4717         sb.append("\n");
4718         Rlog.v(LOG_TAG, sb.toString());
4719     }
4720 
4721     @Override
dump(FileDescriptor fd, PrintWriter printWriter, String[] args)4722     public void dump(FileDescriptor fd, PrintWriter printWriter, String[] args) {
4723         IndentingPrintWriter pw = new IndentingPrintWriter(printWriter, "  ");
4724         pw.println("ImsPhoneCallTracker extends:");
4725         pw.increaseIndent();
4726         super.dump(fd, pw, args);
4727         pw.decreaseIndent();
4728         pw.println(" mVoiceCallEndedRegistrants=" + mVoiceCallEndedRegistrants);
4729         pw.println(" mVoiceCallStartedRegistrants=" + mVoiceCallStartedRegistrants);
4730         pw.println(" mRingingCall=" + mRingingCall);
4731         pw.println(" mForegroundCall=" + mForegroundCall);
4732         pw.println(" mBackgroundCall=" + mBackgroundCall);
4733         pw.println(" mHandoverCall=" + mHandoverCall);
4734         pw.println(" mPendingMO=" + mPendingMO);
4735         pw.println(" mPhone=" + mPhone);
4736         pw.println(" mDesiredMute=" + mDesiredMute);
4737         pw.println(" mState=" + mState);
4738         pw.println(" mMmTelCapabilities=" + mMmTelCapabilities);
4739         pw.println(" mDefaultDialerUid=" + mDefaultDialerUid.get());
4740         pw.println(" mVtDataUsageSnapshot=" + mVtDataUsageSnapshot);
4741         pw.println(" mVtDataUsageUidSnapshot=" + mVtDataUsageUidSnapshot);
4742         pw.println(" mCallQualityMetrics=" + mCallQualityMetrics);
4743         pw.println(" mCallQualityMetricsHistory=" + mCallQualityMetricsHistory);
4744         pw.println(" mIsConferenceEventPackageHandlingEnabled=" + mIsConferenceEventPackageEnabled);
4745         pw.println(" mSupportCepOnPeer=" + mSupportCepOnPeer);
4746         if (mConfig != null) {
4747             pw.print(" isDeviceToDeviceCommsSupported= " + mConfig.isD2DCommunicationSupported);
4748             pw.println("(forceEnabled=" + mDeviceToDeviceForceEnabled + ")");
4749             if (mConfig.isD2DCommunicationSupported) {
4750                 pw.println(" mSupportD2DUsingRtp= " + mSupportD2DUsingRtp);
4751                 pw.println(" mSupportSdpForRtpHeaderExtensions= "
4752                         + mSupportSdpForRtpHeaderExtensions);
4753             }
4754         }
4755         pw.println(" Event Log:");
4756         pw.increaseIndent();
4757         mOperationLocalLog.dump(pw);
4758         pw.decreaseIndent();
4759         pw.flush();
4760         pw.println("++++++++++++++++++++++++++++++++");
4761 
4762         try {
4763             if (mImsManager != null) {
4764                 mImsManager.dump(fd, pw, args);
4765             }
4766         } catch (Exception e) {
4767             e.printStackTrace();
4768         }
4769 
4770         if (mConnections != null && mConnections.size() > 0) {
4771             pw.println("mConnections:");
4772             for (int i = 0; i < mConnections.size(); i++) {
4773                 pw.println("  [" + i + "]: " + mConnections.get(i));
4774             }
4775         }
4776     }
4777 
4778     @Override
handlePollCalls(AsyncResult ar)4779     protected void handlePollCalls(AsyncResult ar) {
4780     }
4781 
4782     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
4783     /* package */
getEcbmInterface()4784     ImsEcbm getEcbmInterface() throws ImsException {
4785         if (mImsManager == null) {
4786             throw getImsManagerIsNullException();
4787         }
4788 
4789         ImsEcbm ecbm = mImsManager.getEcbmInterface();
4790         return ecbm;
4791     }
4792 
isInEmergencyCall()4793     public boolean isInEmergencyCall() {
4794         return mIsInEmergencyCall;
4795     }
4796 
4797     /**
4798      * Contacts the ImsService directly for capability information.  May be slow.
4799      * @return true if the IMS capability for the specified registration technology is currently
4800      * available.
4801      */
isImsCapabilityAvailable(int capability, int regTech)4802     public boolean isImsCapabilityAvailable(int capability, int regTech) throws ImsException {
4803         if (mImsManager != null) {
4804             return mImsManager.queryMmTelCapabilityStatus(capability, regTech);
4805         } else {
4806             return false;
4807         }
4808     }
4809 
4810     /**
4811      * @return {@code true} if voice over cellular is enabled.
4812      */
isVoiceOverCellularImsEnabled()4813     public boolean isVoiceOverCellularImsEnabled() {
4814         return isImsCapabilityInCacheAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
4815                 ImsRegistrationImplBase.REGISTRATION_TECH_LTE)
4816                 || isImsCapabilityInCacheAvailable(
4817                         MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
4818                         ImsRegistrationImplBase.REGISTRATION_TECH_NR);
4819     }
4820 
isVowifiEnabled()4821     public boolean isVowifiEnabled() {
4822         return isImsCapabilityInCacheAvailable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
4823                 ImsRegistrationImplBase.REGISTRATION_TECH_IWLAN)
4824                 || isImsCapabilityInCacheAvailable(
4825                         MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VOICE,
4826                         ImsRegistrationImplBase.REGISTRATION_TECH_CROSS_SIM);
4827     }
4828 
isVideoCallEnabled()4829     public boolean isVideoCallEnabled() {
4830         // Currently no reliance on transport technology.
4831         return mMmTelCapabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_VIDEO);
4832     }
4833 
isImsCapabilityInCacheAvailable(int capability, int regTech)4834     private boolean isImsCapabilityInCacheAvailable(int capability, int regTech) {
4835         return (getImsRegistrationTech() == regTech) && mMmTelCapabilities.isCapable(capability);
4836     }
4837 
4838     @Override
getState()4839     public PhoneConstants.State getState() {
4840         return mState;
4841     }
4842 
getImsRegistrationTech()4843     public int getImsRegistrationTech() {
4844         if (mImsManager != null) {
4845             return mImsManager.getRegistrationTech();
4846         }
4847         return ImsRegistrationImplBase.REGISTRATION_TECH_NONE;
4848     }
4849 
4850     /**
4851      * Asynchronously gets the IMS registration technology for MMTEL.
4852      */
getImsRegistrationTech(Consumer<Integer> callback)4853     public void getImsRegistrationTech(Consumer<Integer> callback) {
4854         if (mImsManager != null) {
4855             mImsManager.getRegistrationTech(callback);
4856         } else {
4857             callback.accept(ImsRegistrationImplBase.REGISTRATION_TECH_NONE);
4858         }
4859     }
4860 
4861     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)4862     private void setVideoCallProvider(ImsPhoneConnection conn, ImsCall imsCall)
4863             throws RemoteException {
4864         IImsVideoCallProvider imsVideoCallProvider =
4865                 imsCall.getCallSession().getVideoCallProvider();
4866         if (imsVideoCallProvider != null) {
4867             // TODO: Remove this when we can better formalize the format of session modify requests.
4868             boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
4869                     com.android.internal.R.bool.config_useVideoPauseWorkaround);
4870 
4871             ImsVideoCallProviderWrapper imsVideoCallProviderWrapper =
4872                     new ImsVideoCallProviderWrapper(imsVideoCallProvider);
4873             if (useVideoPauseWorkaround) {
4874                 imsVideoCallProviderWrapper.setUseVideoPauseWorkaround(useVideoPauseWorkaround);
4875             }
4876             conn.setVideoProvider(imsVideoCallProviderWrapper);
4877             imsVideoCallProviderWrapper.registerForDataUsageUpdate
4878                     (this, EVENT_VT_DATA_USAGE_UPDATE, imsCall);
4879             imsVideoCallProviderWrapper.addImsVideoProviderCallback(conn);
4880         }
4881     }
4882 
isUtEnabled()4883     public boolean isUtEnabled() {
4884         // Currently no reliance on transport technology
4885         return mMmTelCapabilities.isCapable(MmTelFeature.MmTelCapabilities.CAPABILITY_TYPE_UT);
4886     }
4887 
4888     /**
4889      *
4890      * @param subId The subId to get the carrier config for.
4891      * @return The PersistableBundle containing the carrier config  from
4892      * {@link CarrierConfigManager} for the subId specified.
4893      */
getCarrierConfigBundle(int subId)4894     private PersistableBundle getCarrierConfigBundle(int subId) {
4895         CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
4896                 mPhone.getContext().getSystemService(Context.CARRIER_CONFIG_SERVICE);
4897         if (carrierConfigManager == null) {
4898             loge("getCarrierConfigBundle: No carrier config service found");
4899             return null;
4900         }
4901         PersistableBundle carrierConfig = carrierConfigManager.getConfigForSubId(subId);
4902         if (carrierConfig == null) {
4903             loge("getCarrierConfigBundle: carrier config is null, skipping.");
4904             return null;
4905         }
4906         return carrierConfig;
4907     }
4908 
4909     /**
4910      * Given a call subject, removes any characters considered by the current carrier to be
4911      * invalid, as well as escaping (using \) any characters which the carrier requires to be
4912      * escaped.
4913      *
4914      * @param callSubject The call subject.
4915      * @return The call subject with invalid characters removed and escaping applied as required.
4916      */
cleanseInstantLetteringMessage(String callSubject)4917     private String cleanseInstantLetteringMessage(String callSubject) {
4918         if (TextUtils.isEmpty(callSubject)) {
4919             return callSubject;
4920         }
4921 
4922         PersistableBundle carrierConfig = getCarrierConfigBundle(mPhone.getSubId());
4923         // Bail if no carrier config found.
4924         if (carrierConfig == null) {
4925             return callSubject;
4926         }
4927 
4928         // Try to replace invalid characters
4929         String invalidCharacters = carrierConfig.getString(
4930                 CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_INVALID_CHARS_STRING);
4931         if (!TextUtils.isEmpty(invalidCharacters)) {
4932             callSubject = callSubject.replaceAll(invalidCharacters, "");
4933         }
4934 
4935         // Try to escape characters which need to be escaped.
4936         String escapedCharacters = carrierConfig.getString(
4937                 CarrierConfigManager.KEY_CARRIER_INSTANT_LETTERING_ESCAPED_CHARS_STRING);
4938         if (!TextUtils.isEmpty(escapedCharacters)) {
4939             callSubject = escapeChars(escapedCharacters, callSubject);
4940         }
4941         return callSubject;
4942     }
4943 
4944     /**
4945      * Given a source string, return a string where a set of characters are escaped using the
4946      * backslash character.
4947      *
4948      * @param toEscape The characters to escape with a backslash.
4949      * @param source The source string.
4950      * @return The source string with characters escaped.
4951      */
escapeChars(String toEscape, String source)4952     private String escapeChars(String toEscape, String source) {
4953         StringBuilder escaped = new StringBuilder();
4954         for (char c : source.toCharArray()) {
4955             if (toEscape.contains(Character.toString(c))) {
4956                 escaped.append("\\");
4957             }
4958             escaped.append(c);
4959         }
4960 
4961         return escaped.toString();
4962     }
4963 
4964     /**
4965      * Initiates a pull of an external call.
4966      *
4967      * Initiates a pull by making a dial request with the {@link ImsCallProfile#EXTRA_IS_CALL_PULL}
4968      * extra specified.  We call {@link ImsPhone#notifyUnknownConnection(Connection)} which notifies
4969      * Telecom of the new dialed connection.  The
4970      * {@code PstnIncomingCallNotifier#maybeSwapWithUnknownConnection} logic ensures that the new
4971      * {@link ImsPhoneConnection} resulting from the dial gets swapped with the
4972      * {@link ImsExternalConnection}, which effectively makes the external call become a regular
4973      * call.  Magic!
4974      *
4975      * @param number The phone number of the call to be pulled.
4976      * @param videoState The desired video state of the pulled call.
4977      * @param dialogId The {@link ImsExternalConnection#getCallId()} dialog id associated with the
4978      *                 call which is being pulled.
4979      */
4980     @Override
pullExternalCall(String number, int videoState, int dialogId)4981     public void pullExternalCall(String number, int videoState, int dialogId) {
4982         Bundle extras = new Bundle();
4983         extras.putBoolean(ImsCallProfile.EXTRA_IS_CALL_PULL, true);
4984         extras.putInt(ImsExternalCallTracker.EXTRA_IMS_EXTERNAL_CALL_ID, dialogId);
4985         try {
4986             Connection connection = dial(number, videoState, extras);
4987             mPhone.notifyUnknownConnection(connection);
4988         } catch (CallStateException e) {
4989             loge("pullExternalCall failed - " + e);
4990         }
4991     }
4992 
getImsManagerIsNullException()4993     private ImsException getImsManagerIsNullException() {
4994         return new ImsException("no ims manager", ImsReasonInfo.CODE_LOCAL_ILLEGAL_STATE);
4995     }
4996 
4997     /**
4998      * Determines if answering an incoming call will cause the active call to be disconnected.
4999      * <p>
5000      * This will be the case if
5001      * {@link CarrierConfigManager#KEY_DROP_VIDEO_CALL_WHEN_ANSWERING_AUDIO_CALL_BOOL} is
5002      * {@code true} for the carrier, the active call is a video call over WIFI, and the incoming
5003      * call is an audio call.
5004      *
5005      * @param activeCall The active call.
5006      * @param incomingCall The incoming call.
5007      * @return {@code true} if answering the incoming call will cause the active call to be
5008      *      disconnected, {@code false} otherwise.
5009      */
shouldDisconnectActiveCallOnAnswer(ImsCall activeCall, ImsCall incomingCall)5010     private boolean shouldDisconnectActiveCallOnAnswer(ImsCall activeCall,
5011             ImsCall incomingCall) {
5012 
5013         if (activeCall == null || incomingCall == null) {
5014             return false;
5015         }
5016 
5017         if (!mDropVideoCallWhenAnsweringAudioCall) {
5018             return false;
5019         }
5020 
5021         boolean isActiveCallVideo = activeCall.isVideoCall() ||
5022                 (mTreatDowngradedVideoCallsAsVideoCalls && activeCall.wasVideoCall());
5023         boolean isActiveCallOnWifi = activeCall.isWifiCall();
5024         boolean isVoWifiEnabled = mImsManager.isWfcEnabledByPlatform()
5025                 && mImsManager.isWfcEnabledByUser();
5026         boolean isIncomingCallAudio = !incomingCall.isVideoCall();
5027         log("shouldDisconnectActiveCallOnAnswer : isActiveCallVideo=" + isActiveCallVideo +
5028                 " isActiveCallOnWifi=" + isActiveCallOnWifi + " isIncomingCallAudio=" +
5029                 isIncomingCallAudio + " isVowifiEnabled=" + isVoWifiEnabled);
5030 
5031         return isActiveCallVideo && isActiveCallOnWifi && isIncomingCallAudio && !isVoWifiEnabled;
5032     }
5033 
registerPhoneStateListener(PhoneStateListener listener)5034     public void registerPhoneStateListener(PhoneStateListener listener) {
5035         mPhoneStateListeners.add(listener);
5036     }
5037 
unregisterPhoneStateListener(PhoneStateListener listener)5038     public void unregisterPhoneStateListener(PhoneStateListener listener) {
5039         mPhoneStateListeners.remove(listener);
5040     }
5041 
5042     /**
5043      * Notifies local telephony listeners of changes to the IMS phone state.
5044      *
5045      * @param oldState The old state.
5046      * @param newState The new state.
5047      */
notifyPhoneStateChanged(PhoneConstants.State oldState, PhoneConstants.State newState)5048     private void notifyPhoneStateChanged(PhoneConstants.State oldState,
5049             PhoneConstants.State newState) {
5050 
5051         for (PhoneStateListener listener : mPhoneStateListeners) {
5052             listener.onPhoneStateChanged(oldState, newState);
5053         }
5054     }
5055 
5056     /** Modify video call to a new video state.
5057      *
5058      * @param imsCall IMS call to be modified
5059      * @param newVideoState New video state. (Refer to VideoProfile)
5060      */
modifyVideoCall(ImsCall imsCall, int newVideoState)5061     private void modifyVideoCall(ImsCall imsCall, int newVideoState) {
5062         ImsPhoneConnection conn = findConnection(imsCall);
5063         if (conn != null) {
5064             int oldVideoState = conn.getVideoState();
5065             if (conn.getVideoProvider() != null) {
5066                 conn.getVideoProvider().onSendSessionModifyRequest(
5067                         new VideoProfile(oldVideoState), new VideoProfile(newVideoState));
5068             }
5069         }
5070     }
5071 
isViLteDataMetered()5072     public boolean isViLteDataMetered() {
5073         return mIsViLteDataMetered;
5074     }
5075 
5076     /**
5077      * Handler of data enabled changed event
5078      * @param enabled True if data is enabled, otherwise disabled.
5079      * @param reason Reason for data enabled/disabled. See {@link DataEnabledChangedReason}.
5080      */
onDataEnabledChanged(boolean enabled, @DataEnabledChangedReason int reason)5081     private void onDataEnabledChanged(boolean enabled, @DataEnabledChangedReason int reason) {
5082         // TODO: TelephonyManager.DataEnabledChangedReason instead once DataEnabledSettings is gone
5083         log("onDataEnabledChanged: enabled=" + enabled + ", reason=" + reason);
5084 
5085         mIsDataEnabled = enabled;
5086 
5087         if (!mIsViLteDataMetered) {
5088             log("Ignore data " + ((enabled) ? "enabled" : "disabled") + " - carrier policy "
5089                     + "indicates that data is not metered for ViLTE calls.");
5090             return;
5091         }
5092 
5093         // Inform connections that data has been disabled to ensure we turn off video capability
5094         // if this is an LTE call.
5095         for (ImsPhoneConnection conn : mConnections) {
5096             ImsCall imsCall = conn.getImsCall();
5097             boolean isLocalVideoCapable = enabled || (imsCall != null && imsCall.isWifiCall());
5098             conn.setLocalVideoCapable(isLocalVideoCapable);
5099         }
5100 
5101         int reasonCode;
5102         if (reason == DataEnabledSettings.REASON_POLICY_DATA_ENABLED) {
5103             reasonCode = ImsReasonInfo.CODE_DATA_LIMIT_REACHED;
5104         } else if (reason == DataEnabledSettings.REASON_USER_DATA_ENABLED) {
5105             reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
5106         } else {
5107             // Unexpected code, default to data disabled.
5108             reasonCode = ImsReasonInfo.CODE_DATA_DISABLED;
5109         }
5110 
5111         // Potentially send connection events so the InCall UI knows that video calls are being
5112         // downgraded due to data being enabled/disabled.
5113         maybeNotifyDataDisabled(enabled, reasonCode);
5114         // Handle video state changes required as a result of data being enabled/disabled.
5115         handleDataEnabledChange(enabled, reasonCode);
5116 
5117         // We do not want to update the ImsConfig for REASON_REGISTERED, since it can happen before
5118         // the carrier config has loaded and will deregister IMS.
5119         if (!mShouldUpdateImsConfigOnDisconnect
5120                 && reason != DataEnabledSettings.REASON_REGISTERED
5121                 && mCarrierConfigLoadedForSubscription) {
5122             // This will call into updateVideoCallFeatureValue and eventually all clients will be
5123             // asynchronously notified that the availability of VT over LTE has changed.
5124             updateImsServiceConfig();
5125         }
5126     }
5127 
5128     /**
5129      * If the ImsService is currently connected and we have loaded the carrier config, proceed to
5130      * trigger the update of the configuration sent to the ImsService.
5131      */
updateImsServiceConfig()5132     private void updateImsServiceConfig() {
5133         if (mImsManager != null && mCarrierConfigLoadedForSubscription) {
5134             mImsManager.updateImsServiceConfig();
5135         }
5136     }
5137 
maybeNotifyDataDisabled(boolean enabled, int reasonCode)5138     private void maybeNotifyDataDisabled(boolean enabled, int reasonCode) {
5139         if (!enabled) {
5140             // If data is disabled while there are ongoing VT calls which are not taking place over
5141             // wifi, then they should be disconnected to prevent the user from incurring further
5142             // data charges.
5143             for (ImsPhoneConnection conn : mConnections) {
5144                 ImsCall imsCall = conn.getImsCall();
5145                 if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) {
5146                     if (conn.hasCapabilities(
5147                             Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
5148                                     Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)) {
5149 
5150                         // If the carrier supports downgrading to voice, then we can simply issue a
5151                         // downgrade to voice instead of terminating the call.
5152                         if (reasonCode == ImsReasonInfo.CODE_DATA_DISABLED) {
5153                             conn.onConnectionEvent(TelephonyManager.EVENT_DOWNGRADE_DATA_DISABLED,
5154                                     null);
5155                         } else if (reasonCode == ImsReasonInfo.CODE_DATA_LIMIT_REACHED) {
5156                             conn.onConnectionEvent(
5157                                     TelephonyManager.EVENT_DOWNGRADE_DATA_LIMIT_REACHED, null);
5158                         }
5159                     }
5160                 }
5161             }
5162         }
5163     }
5164 
5165     /**
5166      * Handles changes to the enabled state of mobile data.
5167      * When data is disabled, handles auto-downgrade of video calls over LTE.
5168      * When data is enabled, handled resuming of video calls paused when data was disabled.
5169      * @param enabled {@code true} if mobile data is enabled, {@code false} if mobile data is
5170      *                            disabled.
5171      * @param reasonCode The {@link ImsReasonInfo} code for the data enabled state change.
5172      */
handleDataEnabledChange(boolean enabled, int reasonCode)5173     private void handleDataEnabledChange(boolean enabled, int reasonCode) {
5174         if (!enabled) {
5175             // If data is disabled while there are ongoing VT calls which are not taking place over
5176             // wifi, then they should be disconnected to prevent the user from incurring further
5177             // data charges.
5178             for (ImsPhoneConnection conn : mConnections) {
5179                 ImsCall imsCall = conn.getImsCall();
5180                 if (imsCall != null && imsCall.isVideoCall() && !imsCall.isWifiCall()) {
5181                     log("handleDataEnabledChange - downgrading " + conn);
5182                     downgradeVideoCall(reasonCode, conn);
5183                 }
5184             }
5185         } else if (mSupportPauseVideo) {
5186             // Data was re-enabled, so un-pause previously paused video calls.
5187             for (ImsPhoneConnection conn : mConnections) {
5188                 // If video is paused, check to see if there are any pending pauses due to enabled
5189                 // state of data changing.
5190                 log("handleDataEnabledChange - resuming " + conn);
5191                 if (VideoProfile.isPaused(conn.getVideoState()) &&
5192                         conn.wasVideoPausedFromSource(VideoPauseTracker.SOURCE_DATA_ENABLED)) {
5193                     // The data enabled state was a cause of a pending pause, so potentially
5194                     // resume the video now.
5195                     conn.resumeVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
5196                 }
5197             }
5198             mShouldUpdateImsConfigOnDisconnect = false;
5199         }
5200     }
5201 
5202     /**
5203      * Handles downgrading a video call.  The behavior depends on carrier capabilities; we will
5204      * attempt to take one of the following actions (in order of precedence):
5205      * 1. If supported by the carrier, the call will be downgraded to an audio-only call.
5206      * 2. If the carrier supports video pause signalling, the video will be paused.
5207      * 3. The call will be disconnected.
5208      * @param reasonCode The {@link ImsReasonInfo} reason code for the downgrade.
5209      * @param conn The {@link ImsPhoneConnection} to downgrade.
5210      */
downgradeVideoCall(int reasonCode, ImsPhoneConnection conn)5211     private void downgradeVideoCall(int reasonCode, ImsPhoneConnection conn) {
5212         ImsCall imsCall = conn.getImsCall();
5213         if (imsCall != null) {
5214             if (conn.hasCapabilities(
5215                     Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_LOCAL |
5216                             Connection.Capability.SUPPORTS_DOWNGRADE_TO_VOICE_REMOTE)
5217                             && !mSupportPauseVideo) {
5218                 log("downgradeVideoCall :: callId=" + conn.getTelecomCallId()
5219                         + " Downgrade to audio");
5220                 // If the carrier supports downgrading to voice, then we can simply issue a
5221                 // downgrade to voice instead of terminating the call.
5222                 modifyVideoCall(imsCall, VideoProfile.STATE_AUDIO_ONLY);
5223             } else if (mSupportPauseVideo && reasonCode != ImsReasonInfo.CODE_WIFI_LOST) {
5224                 // The carrier supports video pause signalling, so pause the video if we didn't just
5225                 // lose wifi; in that case just disconnect.
5226                 log("downgradeVideoCall :: callId=" + conn.getTelecomCallId()
5227                         + " Pause audio");
5228                 mShouldUpdateImsConfigOnDisconnect = true;
5229                 conn.pauseVideo(VideoPauseTracker.SOURCE_DATA_ENABLED);
5230             } else {
5231                 log("downgradeVideoCall :: callId=" + conn.getTelecomCallId()
5232                         + " Disconnect call.");
5233                 // At this point the only choice we have is to terminate the call.
5234                 imsCall.terminate(ImsReasonInfo.CODE_USER_TERMINATED, reasonCode);
5235             }
5236         }
5237     }
5238 
resetImsCapabilities()5239     private void resetImsCapabilities() {
5240         log("Resetting Capabilities...");
5241         boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
5242         mMmTelCapabilities = new MmTelFeature.MmTelCapabilities();
5243         mPhone.setServiceState(ServiceState.STATE_OUT_OF_SERVICE);
5244         mPhone.resetImsRegistrationState();
5245         mPhone.processDisconnectReason(new ImsReasonInfo(ImsReasonInfo.CODE_LOCAL_IMS_SERVICE_DOWN,
5246                 ImsReasonInfo.CODE_UNSPECIFIED));
5247         boolean isVideoEnabled = isVideoCallEnabled();
5248         if (tmpIsVideoCallEnabled != isVideoEnabled) {
5249             mPhone.notifyForVideoCapabilityChanged(isVideoEnabled);
5250         }
5251     }
5252 
5253     /**
5254      * @return {@code true} if the device is connected to a WIFI network, {@code false} otherwise.
5255      */
isWifiConnected()5256     private boolean isWifiConnected() {
5257         ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
5258                 .getSystemService(Context.CONNECTIVITY_SERVICE);
5259         if (cm != null) {
5260             NetworkInfo ni = cm.getActiveNetworkInfo();
5261             if (ni != null && ni.isConnected()) {
5262                 return ni.getType() == ConnectivityManager.TYPE_WIFI;
5263             }
5264         }
5265         return false;
5266     }
5267 
5268     /**
5269      * Registers for changes to network connectivity.  Specifically requests the availability of new
5270      * WIFI networks which an IMS video call could potentially hand over to.
5271      */
registerForConnectivityChanges()5272     private void registerForConnectivityChanges() {
5273         if (mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) {
5274             return;
5275         }
5276         ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
5277                 .getSystemService(Context.CONNECTIVITY_SERVICE);
5278         if (cm != null) {
5279             Rlog.i(LOG_TAG, "registerForConnectivityChanges");
5280             NetworkRequest.Builder builder = new NetworkRequest.Builder();
5281             builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
5282             cm.registerNetworkCallback(builder.build(), mNetworkCallback);
5283             mIsMonitoringConnectivity = true;
5284         }
5285     }
5286 
5287     /**
5288      * Unregister for connectivity changes.  Will be called when a call disconnects or if the call
5289      * ends up handing over to WIFI.
5290      */
unregisterForConnectivityChanges()5291     private void unregisterForConnectivityChanges() {
5292         if (!mIsMonitoringConnectivity || !mNotifyVtHandoverToWifiFail) {
5293             return;
5294         }
5295         ConnectivityManager cm = (ConnectivityManager) mPhone.getContext()
5296                 .getSystemService(Context.CONNECTIVITY_SERVICE);
5297         if (cm != null) {
5298             Rlog.i(LOG_TAG, "unregisterForConnectivityChanges");
5299             cm.unregisterNetworkCallback(mNetworkCallback);
5300             mIsMonitoringConnectivity = false;
5301         }
5302     }
5303 
5304     /**
5305      * If the foreground call is a video call, schedule a handover check if one is not already
5306      * scheduled.  This method is intended ONLY for use when scheduling to watch for mid-call
5307      * handovers.
5308      */
scheduleHandoverCheck()5309     private void scheduleHandoverCheck() {
5310         ImsCall fgCall = mForegroundCall.getImsCall();
5311         ImsPhoneConnection conn = mForegroundCall.getFirstConnection();
5312         if (!mNotifyVtHandoverToWifiFail || fgCall == null || !fgCall.isVideoCall() || conn == null
5313                 || conn.getDisconnectCause() != DisconnectCause.NOT_DISCONNECTED) {
5314             return;
5315         }
5316 
5317         if (!hasMessages(EVENT_CHECK_FOR_WIFI_HANDOVER)) {
5318             Rlog.i(LOG_TAG, "scheduleHandoverCheck: schedule");
5319             sendMessageDelayed(obtainMessage(EVENT_CHECK_FOR_WIFI_HANDOVER, fgCall),
5320                     HANDOVER_TO_WIFI_TIMEOUT_MS);
5321         }
5322     }
5323 
5324     /**
5325      * @return {@code true} if downgrading of a video call to audio is supported.
5326          */
isCarrierDowngradeOfVtCallSupported()5327     public boolean isCarrierDowngradeOfVtCallSupported() {
5328         return mSupportDowngradeVtToAudio;
5329     }
5330 
5331     @VisibleForTesting
setDataEnabled(boolean isDataEnabled)5332     public void setDataEnabled(boolean isDataEnabled) {
5333         mIsDataEnabled = isDataEnabled;
5334     }
5335 
5336     // Removes old call quality metrics if mCallQualityMetricsHistory exceeds its max size
pruneCallQualityMetricsHistory()5337     private void pruneCallQualityMetricsHistory() {
5338         if (mCallQualityMetricsHistory.size() > MAX_CALL_QUALITY_HISTORY) {
5339             mCallQualityMetricsHistory.poll();
5340         }
5341     }
5342 
handleFeatureCapabilityChanged(ImsFeature.Capabilities capabilities)5343     private void handleFeatureCapabilityChanged(ImsFeature.Capabilities capabilities) {
5344         boolean tmpIsVideoCallEnabled = isVideoCallEnabled();
5345         // Check enabledFeatures to determine capabilities. We ignore disabledFeatures.
5346         StringBuilder sb;
5347         if (DBG) {
5348             sb = new StringBuilder(120);
5349             sb.append("handleFeatureCapabilityChanged: ");
5350         }
5351         sb.append(capabilities);
5352         mMmTelCapabilities = new MmTelFeature.MmTelCapabilities(capabilities);
5353 
5354         boolean isVideoEnabled = isVideoCallEnabled();
5355         boolean isVideoEnabledStatechanged = tmpIsVideoCallEnabled != isVideoEnabled;
5356         if (DBG) {
5357             sb.append(" isVideoEnabledStateChanged=");
5358             sb.append(isVideoEnabledStatechanged);
5359         }
5360 
5361         if (isVideoEnabledStatechanged) {
5362             log("handleFeatureCapabilityChanged - notifyForVideoCapabilityChanged="
5363                     + isVideoEnabled);
5364             mPhone.notifyForVideoCapabilityChanged(isVideoEnabled);
5365         }
5366 
5367         if (DBG) log(sb.toString());
5368 
5369         String logMessage = "handleFeatureCapabilityChanged: isVolteEnabled="
5370                 + isVoiceOverCellularImsEnabled()
5371                 + ", isVideoCallEnabled=" + isVideoCallEnabled()
5372                 + ", isVowifiEnabled=" + isVowifiEnabled()
5373                 + ", isUtEnabled=" + isUtEnabled();
5374         if (DBG) {
5375             log(logMessage);
5376         }
5377         mRegLocalLog.log(logMessage);
5378 
5379         mPhone.onFeatureCapabilityChanged();
5380 
5381         int regTech = getImsRegistrationTech();
5382         mMetrics.writeOnImsCapabilities(mPhone.getPhoneId(), regTech, mMmTelCapabilities);
5383         mPhone.getImsStats().onImsCapabilitiesChanged(regTech, mMmTelCapabilities);
5384     }
5385 
5386     @VisibleForTesting
onCallHoldReceived(ImsCall imsCall)5387     public void onCallHoldReceived(ImsCall imsCall) {
5388         if (DBG) log("onCallHoldReceived");
5389 
5390         ImsPhoneConnection conn = findConnection(imsCall);
5391         if (conn != null) {
5392             if (!mOnHoldToneStarted && (ImsPhoneCall.isLocalTone(imsCall)
5393                     || mAlwaysPlayRemoteHoldTone) &&
5394                     conn.getState() == ImsPhoneCall.State.ACTIVE) {
5395                 mPhone.startOnHoldTone(conn);
5396                 mOnHoldToneStarted = true;
5397                 mOnHoldToneId = System.identityHashCode(conn);
5398             }
5399             conn.onConnectionEvent(android.telecom.Connection.EVENT_CALL_REMOTELY_HELD, null);
5400 
5401             boolean useVideoPauseWorkaround = mPhone.getContext().getResources().getBoolean(
5402                     com.android.internal.R.bool.config_useVideoPauseWorkaround);
5403             if (useVideoPauseWorkaround && mSupportPauseVideo &&
5404                     VideoProfile.isVideo(conn.getVideoState())) {
5405                 // If we are using the video pause workaround, the vendor IMS code has issues
5406                 // with video pause signalling.  In this case, when a call is remotely
5407                 // held, the modem does not reliably change the video state of the call to be
5408                 // paused.
5409                 // As a workaround, we will turn on that bit now.
5410                 conn.changeToPausedState();
5411             }
5412         }
5413 
5414         SuppServiceNotification supp = new SuppServiceNotification();
5415         supp.notificationType = SuppServiceNotification.NOTIFICATION_TYPE_CODE_2;
5416         supp.code = SuppServiceNotification.CODE_2_CALL_ON_HOLD;
5417         mPhone.notifySuppSvcNotification(supp);
5418         mMetrics.writeOnImsCallHoldReceived(mPhone.getPhoneId(), imsCall.getCallSession());
5419     }
5420 
5421     @VisibleForTesting
setAlwaysPlayRemoteHoldTone(boolean shouldPlayRemoteHoldTone)5422     public void setAlwaysPlayRemoteHoldTone(boolean shouldPlayRemoteHoldTone) {
5423         mAlwaysPlayRemoteHoldTone = shouldPlayRemoteHoldTone;
5424     }
5425 
getNetworkCountryIso()5426     private String getNetworkCountryIso() {
5427         String countryIso = "";
5428         if (mPhone != null) {
5429             ServiceStateTracker sst = mPhone.getServiceStateTracker();
5430             if (sst != null) {
5431                 LocaleTracker lt = sst.getLocaleTracker();
5432                 if (lt != null) {
5433                     countryIso = lt.getCurrentCountry();
5434                 }
5435             }
5436         }
5437         return countryIso;
5438     }
5439 
5440     @Override
getPhone()5441     public ImsPhone getPhone() {
5442         return mPhone;
5443     }
5444 
5445     @VisibleForTesting
setSupportCepOnPeer(boolean isSupported)5446     public void setSupportCepOnPeer(boolean isSupported) {
5447         mSupportCepOnPeer = isSupported;
5448     }
5449 
5450     /**
5451      * Injects a test conference state into an ongoing IMS call.
5452      * @param state The injected state.
5453      */
injectTestConferenceState(@onNull ImsConferenceState state)5454     public void injectTestConferenceState(@NonNull ImsConferenceState state) {
5455         List<ConferenceParticipant> participants = ImsCall.parseConferenceState(state);
5456         for (ImsPhoneConnection connection : getConnections()) {
5457             connection.updateConferenceParticipants(participants);
5458         }
5459     }
5460 
5461     /**
5462      * Sets whether CEP handling is enabled or disabled.
5463      * @param isEnabled
5464      */
setConferenceEventPackageEnabled(boolean isEnabled)5465     public void setConferenceEventPackageEnabled(boolean isEnabled) {
5466         log("setConferenceEventPackageEnabled isEnabled=" + isEnabled);
5467         mIsConferenceEventPackageEnabled = isEnabled;
5468     }
5469 
5470     /**
5471      * @return {@code true} is conference event package handling is enabled, {@code false}
5472      * otherwise.
5473      */
isConferenceEventPackageEnabled()5474     public boolean isConferenceEventPackageEnabled() {
5475         return mIsConferenceEventPackageEnabled;
5476     }
5477 
5478     @VisibleForTesting
getImsCallListener()5479     public ImsCall.Listener getImsCallListener() {
5480         return mImsCallListener;
5481     }
5482 
5483     @VisibleForTesting
getConnections()5484     public ArrayList<ImsPhoneConnection> getConnections() {
5485         return mConnections;
5486     }
5487 
5488     @VisibleForTesting
getPendingMO()5489     public ImsPhoneConnection getPendingMO() {
5490         return mPendingMO;
5491     }
5492 
5493     /**
5494      * Set up static configuration from package/services/Telephony's config.xml.
5495      * @param config the config.
5496      */
setConfig(@onNull Config config)5497     public void setConfig(@NonNull Config config) {
5498         mConfig = config;
5499     }
5500 
handleConferenceFailed(ImsPhoneConnection fgConnection, ImsPhoneConnection bgConnection)5501     private void handleConferenceFailed(ImsPhoneConnection fgConnection,
5502             ImsPhoneConnection bgConnection) {
5503         if (fgConnection != null) {
5504             fgConnection.handleMergeComplete();
5505         }
5506         if (bgConnection != null) {
5507             bgConnection.handleMergeComplete();
5508         }
5509         mPhone.notifySuppServiceFailed(Phone.SuppService.CONFERENCE);
5510     }
5511 
5512     /**
5513      * Calculate whether CSFB or not with fg call type and bg call type.
5514      * @return {@code true} if bg call is not alive or fg call has higher score than bg call.
5515      */
isForegroundHigherPriority()5516     private boolean isForegroundHigherPriority() {
5517         if (!mBackgroundCall.getState().isAlive()) {
5518             return true;
5519         }
5520         ImsPhoneConnection fgConnection = mForegroundCall.getFirstConnection();
5521         ImsPhoneConnection bgConnection = mBackgroundCall.getFirstConnection();
5522         if (fgConnection.getCallPriority() > bgConnection.getCallPriority()) {
5523             return true;
5524         }
5525         return false;
5526     }
5527 }
5528