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