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