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